URL Decode JavaScript — decodeURIComponent()

·Front-end & Node.js Developer·Sprawdzono przezMarcus Webb·Opublikowano

Użyj darmowego Dekoder URL Online bezpośrednio w przeglądarce — bez instalacji.

Wypróbuj Dekoder URL Online online →

Ciągi zakodowane procentowo pojawiają się w kodzie JavaScript nieustannie — zapytanie wyszukiwania przybywa jako q=standing+desk%26price%3A200, przekierowanie OAuth jako next=https%3A%2F%2Fdashboard.internal%2F, ścieżka do pliku jako reports%2F2025%2Fq1.pdf. Dekodowanie URL w JavaScript sprowadza się do wyboru właściwej spośród trzech wbudowanych funkcji: decodeURIComponent(), decodeURI() i URLSearchParams — wybór między nimi jest główną przyczyną większości cichych uszkodzeń danych, jakie widziałem w produkcyjnych bazach kodu, szczególnie przypadek krawędziowy ze znakiem + jako spacja i podwójne dekodowanie. Aby szybko jednorazowo zdekodować bez pisania kodu, Dekoder URL ToolDeck obsługuje to natychmiast w przeglądarce. Ten samouczek JavaScript dekodowania URL omawia wszystkie trzy funkcje dogłębnie (ES2015+ / Node.js 10+): kiedy używać każdej z nich, jak różnią się obsługą spacji i znaków zastrzeżonych, dekodowanie z plików i żądań HTTP, bezpieczna obsługa błędów i cztery błędy powodujące najbardziej subtelne błędy produkcyjne.

  • decodeURIComponent() dekoduje wszystkie sekwencje zakodowane procentowo — to właściwy wybór dla wartości poszczególnych parametrów zapytania i segmentów ścieżki
  • decodeURI() zachowuje strukturalne znaki URI takie jak / ? & = # : — używaj go tylko do dekodowania pełnego ciągu URL, nigdy dla pojedynczych wartości
  • URLSearchParams.get() zwraca już zdekodowane wartości — wywołanie decodeURIComponent() na jego wyniku powoduje podwójne dekodowanie
  • decodeURIComponent() NIE dekoduje + jako spacji — dla danych zakodowanych w formularzach (application/x-www-form-urlencoded) użyj URLSearchParams lub zastąp + przed dekodowaniem
  • Zawsze owijaj decodeURIComponent() w try/catch gdy dane wejściowe pochodzą od użytkownika — sam znak % lub niepełna sekwencja zgłasza URIError

Czym jest dekodowanie URL?

Kodowanie procentowe (formalnie zdefiniowane w RFC 3986) zastępuje znaki, które są niebezpieczne lub mają strukturalne znaczenie w URL, znakiem % i dwoma cyframi szesnastkowymi — wartością bajtu UTF-8 danego znaku. Dekodowanie URL odwraca tę transformację: każda sekwencja %XX jest konwertowana z powrotem do oryginalnego bajtu, a wynikowa sekwencja bajtów jest interpretowana jako tekst UTF-8. Spacja jest dekodowana z %20, ukośnik z %2F, znak spoza ASCII ü z %C3%BC (jego dwubajtowa reprezentacja UTF-8).

Znaki, które nigdy nie są kodowane — zwane znakami niezastrzeżonymi — to litery A–Z i a–z, cyfry 0–9 oraz - _ . ~. Wszystko inne albo ma strukturalną rolę w URL (jak / oddzielający segmenty ścieżki lub & oddzielający parametry zapytania) albo musi być zakodowane gdy używane jako dane. Praktyczny rezultat: filtr wyszukiwania jak status=active&tier=premium zakodowany jako wartość pojedynczego parametru zapytania wygląda zupełnie inaczej niż oryginał.

Before · text
After · text
// Zakodowany procentowo — tak jak otrzymano w żądaniu HTTP lub ładunku webhook
"q=price%3A%5B200+TO+800%5D%20AND%20brand%3ANorthwood%20%26%20status%3Ain-stock"
// Po dekodowaniu URL — oryginalny filtr zapytania Elasticsearch
"q=price:[200 TO 800] AND brand:Northwood & status:in-stock"

decodeURIComponent() — Standardowa Funkcja do Dekodowania Wartości

decodeURIComponent() to główna funkcja robocza dekodowania URL w JavaScript. Dekoduje każdą sekwencję %XX w ciągu wejściowym — w tym znaki mające strukturalne znaczenie w URL, takie jak %2F (ukośnik), %3F (znak zapytania), %26 (ampersand) i %3D (znak równości). Czyni ją to właściwym wyborem do dekodowania poszczególnych wartości parametrów zapytania i segmentów ścieżki, ale złym wyborem do dekodowania pełnego URL — gdzie te strukturalne znaki muszą pozostać zakodowane. Jest to funkcja globalna: żaden import nie jest wymagany w żadnym środowisku JavaScript.

Minimalny działający przykład

JavaScript (browser / Node.js)
// Dekodowanie poszczególnych wartości parametrów zapytania — typowy przypadek

const city     = decodeURIComponent('S%C3%A3o%20Paulo')          // 'São Paulo'
const district = decodeURIComponent('It%C3%A1im%20Bibi')         // 'Itáim Bibi'
const category = decodeURIComponent('office%20furniture')         // 'office furniture'
const filter   = decodeURIComponent('price%3A%5B200+TO+800%5D')  // 'price:[200+TO+800]'
// Uwaga: + NIE jest dekodowany jako spacja — patrz sekcja + w tabeli porównawczej

console.log(city)      // São Paulo
console.log(district)  // Itáim Bibi
console.log(category)  // office furniture
console.log(filter)    // price:[200+TO+800]

Dekodowanie URL przekierowania wyodrębnionego z parametru zapytania

JavaScript
// URL przekierowania był zakodowany procentowo gdy był osadzony jako wartość parametru
// strona odbierająca: wyodrębnij, a potem użyj — bez ręcznego dekodowania przy URLSearchParams

const incomingUrl = 'https://auth.company.com/callback' +
  '?next=https%3A%2F%2Fdashboard.internal%2Freports%3Fview%3Dweekly%26team%3Dplatform' +
  '&session_id=sid_7x9p2k'

const url       = new URL(incomingUrl)
const rawNext   = url.searchParams.get('next')       // Automatycznie zdekodowany przez URLSearchParams
const sessionId = url.searchParams.get('session_id') // 'sid_7x9p2k'

// rawNext jest już zdekodowany: 'https://dashboard.internal/reports?view=weekly&team=platform'
// NIE wywołuj decodeURIComponent(rawNext) ponownie — to byłoby podwójne dekodowanie

const nextUrl = new URL(rawNext!)
console.log(nextUrl.hostname)                    // dashboard.internal
console.log(nextUrl.searchParams.get('view'))    // weekly
console.log(nextUrl.searchParams.get('team'))    // platform

Dekodowanie segmentów ścieżki spoza ASCII i Unicode

JavaScript
// REST API z umiędzynarodowionymi segmentami ścieżki
// Każdy bajt UTF-8 oryginalnego znaku był kodowany procentowo osobno

const encodedSegments = [
  '%E6%9D%B1%E4%BA%AC',   // 東京  (Tokio)   — 3 bajty na znak
  'M%C3%BCnchen',         // München          — ü zakodowane jako 2 bajty
  'caf%C3%A9',            // café             — é jako złożone NFC
  'S%C3%A3o%20Paulo',     // São Paulo
]

encodedSegments.forEach(seg => {
  console.log(decodeURIComponent(seg))
})
// 東京
// München
// café
// São Paulo

// Wyodrębnianie klucza pliku z URL API przechowywania
// Klucz obiektu zawierał / więc był zakodowany jako %2F wewnątrz segmentu ścieżki
const storageUrl = 'https://storage.api.example.com/v1/objects/reports%2F2025%2Fq1-financials.pdf'
const rawKey     = new URL(storageUrl).pathname.replace('/v1/objects/', '')
// .pathname dekoduje kodowanie na poziomie URL ale %2F (jako %252F na poziomie URL) pozostaje
// Użyj decodeURIComponent dla końcowego kroku:
const fileKey = decodeURIComponent(rawKey)  // 'reports/2025/q1-financials.pdf'
console.log(fileKey)
Uwaga:decodeURIComponent() zgłasza URIError gdy dane wejściowe zawierają % nie poprzedzony dwoma prawidłowymi cyframi szesnastkowymi — na przykład sam znak % na końcu ciągu lub sekwencja taka jak %GH. Zawsze owijaj w try/catch przy dekodowaniu danych wejściowych dostarczonych przez użytkownika. Wzorce bezpiecznego dekodowania są omówione w sekcji Obsługa Błędów poniżej.

Funkcje Dekodowania URL w JavaScript — Tabela Znaków

Trzy wbudowane funkcje dekodowania różnią się dokładnie tym, jakie zakodowane sekwencje dekodują. Tabela pokazuje zachowanie dla znaków najważniejszych w praktyce:

ZakodowanyZnakdecodeURIComponent()decodeURI()URLSearchParams
%20spacjaspacja ✅spacja ✅spacja ✅
++ (formularz)+ (zachowany)+ (zachowany)spacja ✅
%2B+ dosłownie+ ✅+ ✅+ ✅
%26&& ✅& (zachowany) ❌& ✅
%3D== ✅= (zachowany) ❌= ✅
%3F?? ✅? (zachowany) ❌? ✅
%23## ✅# (zachowany) ❌# ✅
%2F// ✅/ (zachowany) ❌/ ✅
%3A:: ✅: (zachowany) ❌: ✅
%40@@ ✅@ (zachowany) ❌@ ✅
%25%% ✅% ✅% ✅
%C3%BCüü ✅ü ✅ü ✅

Dwa kluczowe wiersze to + i znaki strukturalne (%26, %3D, %3F). URLSearchParams dekoduje + jako spację, ponieważ przestrzega specyfikacji application/x-www-form-urlencoded — poprawnie dla zgłoszeń formularzy HTML, ale inaczej niż decodeURIComponent() obsługuje ten sam znak. A decodeURI() po cichu pomija %26, %3D i %3F — co wygląda poprawnie dopóki wartość nie zawiera zakodowanego ampersanda lub znaku równości.

decodeURI() — Dekodowanie Pełnego URL bez Niszczenia Struktury

decodeURI() jest odpowiednikiem encodeURI(). Dekoduje pełny ciąg URLzachowując znaki mające strukturalne znaczenie w URI: ; , / ? : @ & = + $ #. Są one pozostawiane w formie zakodowanej procentowo (lub jako znaki dosłowne jeśli pojawiły się niezakodowane w danych wejściowych). Czyni to decodeURI() bezpiecznym do sanityzacji pełnych URL które mogą zawierać znaki spoza ASCII w ścieżce lub nazwie hosta, bez przypadkowego niszczenia struktury ciągu zapytania.

Sanityzacja URL z segmentami ścieżki spoza ASCII

JavaScript
// URL CDN z umiędzynarodowionymi segmentami ścieżki i strukturalnym ciągiem zapytania
// decodeURI() dekoduje ścieżkę spoza ASCII ale zachowuje ? & = nienaruszone

const encodedUrl =
  'https://cdn.example.com/assets/%E6%9D%B1%E4%BA%AC%2F2025%2Fq1-report.pdf' +
  '?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600'

const readable = decodeURI(encodedUrl)
console.log(readable)
// https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600
// ↑ Znaki spoza ASCII zdekodowane; ? & = zachowane — URL pozostaje strukturalnie ważny

// decodeURIComponent zniszczyłby URL — : / ? & = wszystkie zdekodowane naraz
const broken = decodeURIComponent(encodedUrl)
// 'https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600'
// Wygląda tak samo tutaj, ale URL jak 'https%3A%2F%2F...' zostałby zniszczony
Uwaga:Preferuj konstruktor URL zamiast decodeURI() gdy musisz też uzyskać dostęp do poszczególnych komponentów URL. new URL(str) normalizuje dane wejściowe, weryfikuje ich strukturę i udostępnia .pathname, .searchParams i .hostname jako już zdekodowane właściwości. Zachowaj decodeURI() dla przypadków gdy potrzebujesz tylko wyniku w postaci ciągu i nie możesz użyć konstruktora URL (na przykład w bardzo starych środowiskach Node.js 6 bez globalnej klasy URL).

URLSearchParams — Automatyczne Dekodowanie Ciągów Zapytania

URLSearchParams to idiomatyczny sposób parsowania ciągów zapytania w nowoczesnym JavaScript. Każda wartość zwrócona przez .get(), .getAll() lub iterację jest automatycznie dekodowana — w tym + jako spacja dla danych zakodowanych formularzami. Jest dostępna globalnie we wszystkich nowoczesnych przeglądarkach i Node.js 10+, nie wymaga importu i obsługuje przypadki brzegowe, które ręczne dzielenie ciągów obsługuje niepoprawnie.

Parsowanie przychodzącego ciągu zapytania

JavaScript (browser / Node.js 10+)
// Parsowanie ciągu zapytania callbacku webhook lub przekierowania OAuth
const rawSearch =
  '?event_id=evt_9c2f4a1b' +
  '&product_name=Standing+Desk+Pro' +
  '&filter=price%3A%5B200+TO+800%5D' +
  '&tag=ergonomic&tag=adjustable' +
  '&redirect=https%3A%2F%2Fdashboard.internal%2Forders%3Fview%3Dpending'

const params = new URLSearchParams(rawSearch)

console.log(params.get('event_id'))      // 'evt_9c2f4a1b'
console.log(params.get('product_name'))  // 'Standing Desk Pro'       ← + zdekodowany jako spacja
console.log(params.get('filter'))        // 'price:[200+TO+800]'      ← %3A i %5B zdekodowane
console.log(params.getAll('tag'))        // ['ergonomic', 'adjustable']
console.log(params.get('redirect'))      // 'https://dashboard.internal/orders?view=pending'

// Iteracja po wszystkich parametrach
for (const [key, value] of params) {
  console.log(`${key}: ${value}`)
}
// event_id: evt_9c2f4a1b
// product_name: Standing Desk Pro
// filter: price:[200+TO+800]
// tag: ergonomic
// tag: adjustable
// redirect: https://dashboard.internal/orders?view=pending

Parsowanie parametrów zapytania z bieżącego URL przeglądarki

JavaScript (browser)
// Bieżący URL: https://app.example.com/search
//   ?q=standing+desk
//   &category=office+furniture
//   &sort=price_asc
//   &page=2

interface SearchFilters {
  query:    string | null
  category: string | null
  sort:     string
  page:     number
}

function getSearchFilters(): SearchFilters {
  const params = new URLSearchParams(window.location.search)
  return {
    query:    params.get('q'),                       // 'standing desk'
    category: params.get('category'),                // 'office furniture'
    sort:     params.get('sort') ?? 'relevance',
    page:     Number(params.get('page') ?? '1'),
  }
}

const filters = getSearchFilters()
console.log(filters.query)     // standing desk   (+ automatycznie zdekodowany)
console.log(filters.category)  // office furniture

Dekodowanie Danych Zakodowanych URL z Plików i Odpowiedzi API

Dwa scenariusze pojawiają się stale w rzeczywistych projektach: przetwarzanie pliku na dysku zawierającego dane zakodowane procentowo (logi dostępu, eksporty danych, pliki przechwytywania webhook) i parsowanie URL przychodzącego żądania HTTP w serwerze Node.js. Oba kierują się tą samą zasadą — używaj konstruktora URL lub URLSearchParams zamiast ręcznego dzielenia ciągów — ale szczegóły się różnią.

Odczytywanie i dekodowanie rekordów zakodowanych URL z pliku

JavaScript (Node.js 10+)
import { createReadStream } from 'fs'
import { createInterface } from 'readline'

// Plik: orders-export.txt — jeden rekord zakodowany URL na wiersz
// customer_name=Piotr+Kowalski&order_id=ord_9c2f4a&product=Standing+Desk+Pro&total=649%20PLN
// customer_name=Anna+Wi%C5%9Bniewska&order_id=ord_7b3a1c&product=Ergonomic+Chair&total=449%20PLN
// customer_name=Gda%C5%84ski+Klient&order_id=ord_2e8d5f&product=Monitor+Arm&total=299%20PLN

interface Order {
  customerName: string | null
  orderId:      string | null
  product:      string | null
  total:        string | null
}

async function parseOrdersFile(filePath: string): Promise<Order[]> {
  const fileStream = createReadStream(filePath, { encoding: 'utf-8' })
  const rl         = createInterface({ input: fileStream, crlfDelay: Infinity })
  const orders: Order[] = []

  for await (const line of rl) {
    if (!line.trim()) continue

    // URLSearchParams dekoduje + jako spację i sekwencje %XX automatycznie
    const params = new URLSearchParams(line)

    orders.push({
      customerName: params.get('customer_name'),  // 'Piotr Kowalski'
      orderId:      params.get('order_id'),        // 'ord_9c2f4a'
      product:      params.get('product'),         // 'Standing Desk Pro'
      total:        params.get('total'),           // '649 PLN'
    })
  }

  return orders
}

const orders = await parseOrdersFile('./orders-export.txt')
console.log(orders[0])
// { customerName: 'Piotr Kowalski', orderId: 'ord_9c2f4a', product: 'Standing Desk Pro', total: '649 PLN' }

Parsowanie logu dostępu Nginx do dekodowania zapytań wyszukiwania

JavaScript (Node.js)
import { createReadStream } from 'fs'
import { createInterface } from 'readline'

// Wiersze logu dostępu Nginx wyglądają tak:
// 192.168.1.42 - - [11/Mar/2026:10:23:01 +0000] "GET /api/search?q=standing%20desk%26brand%3ANorthwood&sort=price_asc HTTP/1.1" 200 1842

async function extractSearchQueries(logFile: string): Promise<string[]> {
  const rl     = createInterface({ input: createReadStream(logFile), crlfDelay: Infinity })
  const queries: string[] = []

  for await (const line of rl) {
    // Wyodrębnij ścieżkę żądania z wiersza logu
    const match = line.match(/"GET ([^ ]+) HTTP/)
    if (!match) continue

    try {
      const requestUrl = new URL(match[1], 'http://localhost')
      const query      = requestUrl.searchParams.get('q')
      if (query) queries.push(query)  // URLSearchParams dekoduje automatycznie
    } catch {
      // Pomiń zniekształcone wiersze — logi dostępu mogą zawierać obcięte wpisy
    }
  }

  return queries
}

const queries = await extractSearchQueries('/var/log/nginx/access.log')
console.log(queries)
// ['standing desk&brand:Northwood', 'ergonomic chair', 'monitor arm 27 inch']

Parsowanie parametrów zapytania w serwerze HTTP Node.js

JavaScript (Node.js 10+)
import http from 'http'

// Przychodzący URL: /api/products?q=standing+desk&warehouse=eu%2Dwest%2D1&minStock=10&cursor=eyJpZCI6MTIzfQ%3D%3D

const server = http.createServer((req, res) => {
  // Skonstruuj pełny URL — drugi arg wymagany przez konstruktor URL to baza
  const requestUrl = new URL(req.url!, 'http://localhost')

  const searchQuery = requestUrl.searchParams.get('q')            // 'standing desk'
  const warehouseId = requestUrl.searchParams.get('warehouse')    // 'eu-west-1'
  const minStock    = Number(requestUrl.searchParams.get('minStock') ?? '0')
  const cursor      = requestUrl.searchParams.get('cursor')       // 'eyJpZCI6MTIzfQ=='

  if (!warehouseId) {
    res.writeHead(400, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ error: 'warehouse parameter is required' }))
    return
  }

  const cursorData = cursor ? JSON.parse(Buffer.from(cursor, 'base64').toString()) : null

  res.writeHead(200, { 'Content-Type': 'application/json' })
  res.end(JSON.stringify({ searchQuery, warehouseId, minStock, cursorData }))
})

server.listen(3000)

Gdy podczas programowania musisz sprawdzić zakodowany URL — żeby zrozumieć co wysyła webhook przed napisaniem kodu parsującego — wklej go bezpośrednio do Dekodera URL ToolDeck aby zobaczyć zdekodowaną formę natychmiast bez uruchamiania skryptu.

Dekodowanie URL w Wierszu Poleceń

Do skryptów powłoki, potoków CI lub szybkiej jednorazowej inspekcji zakodowanych ciągów kilka podejść działa bez pisania pełnego skryptu. Jednolinijkowe polecenia Node.js są wieloplatformowe; na macOS i Linux python3 jest też zawsze dostępny.

bash
# ── Jednolinijkowe polecenia Node.js ────────────────────────────────────

# Dekodowanie pojedynczej wartości zakodowanej procentowo
node -e "console.log(decodeURIComponent(process.argv[1]))" "S%C3%A3o%20Paulo%20%26%20Rio"
# São Paulo & Rio

# Parsowanie ciągu zapytania i wydrukowanie każdej pary klucz=wartość (zdekodowanej)
node -e "
  const params = new URLSearchParams(process.argv[1])
  for (const [k, v] of params) console.log(`${k} = ${v}`)
" "q=standing+desk&warehouse=eu%2Dwest%2D1&minStock=10"
# q = standing desk
# warehouse = eu-west-1
# minStock = 10

# Dekodowanie i ładne wydrukowanie ciała JSON zakodowanego URL (typowe przy debugowaniu webhook)
node -e "
  const raw = decodeURIComponent(process.argv[1])
  console.log(JSON.stringify(JSON.parse(raw), null, 2))
" '%7B%22event%22%3A%22purchase%22%2C%22amount%22%3A149.99%2C%22currency%22%3A%22EUR%22%7D'
# {
#   "event": "purchase",
#   "amount": 149.99,
#   "currency": "EUR"
# }

# ── Jednolinijkowe polecenie Python (dostępne w większości systemów macOS/Linux) ──────
# unquote_plus dekoduje też + jako spację — poprawne dla danych zakodowanych formularzami
python3 -c "
from urllib.parse import unquote_plus
import sys
print(unquote_plus(sys.argv[1]))
" "Standing+Desk+%26+Ergonomic+Chair"
# Standing Desk & Ergonomic Chair

# ── curl — zarejestruj i zdekoduj URL przekierowania z nagłówka odpowiedzi ──────
curl -sI "https://api.example.com/short/abc123" | grep -i location |   node -e "
    const line = require('fs').readFileSync('/dev/stdin', 'utf8')
    const url  = line.replace(/^location:s*/i, '').trim()
    console.log(decodeURIComponent(url))
  "

Eleganckie Dekodowanie z decode-uri-component

Wbudowany decodeURIComponent() zgłasza URIError dla każdej zniekształconej sekwencji — w tym końcowy % bez dwóch następujących cyfr szesnastkowych. W kodzie produkcyjnym przetwarzającym URL dostarczone przez użytkownika lub strony trzecie — logi zapytań wyszukiwania, URL kliknięć z kampanii emailowych, zeskrobane dane webowe — zniekształcone sekwencje są wystarczająco powszechne, żeby powodować awarie. Pakiet decode-uri-component (~30M tygodniowych pobrań npm) obsługuje zniekształcone sekwencje elegancko, zwracając oryginalną sekwencję bez zmian zamiast zgłaszać wyjątek.

bash
npm install decode-uri-component
# or
pnpm add decode-uri-component
JavaScript (Node.js)
import decodeUriComponent from 'decode-uri-component'

// Natywna funkcja zgłasza przy zniekształconym wejściu — może zawiesić obsługę żądania
try {
  decodeURIComponent('product%name%')   // ❌ URIError: URI malformed
} catch (e) {
  console.error('Natywna zgłosiła:', (e as Error).message)
}

// decode-uri-component zwraca surową sekwencję zamiast zgłaszać
console.log(decodeUriComponent('product%name%'))
// product%name%   ← zniekształcone sekwencje pozostawione bez zmian, brak wyjątku

// Prawidłowe sekwencje są dekodowane poprawnie
console.log(decodeUriComponent('S%C3%A3o%20Paulo%20%26%20Rio'))
// São Paulo & Rio

// Mieszane: prawidłowe sekwencje zdekodowane, nieprawidłowe zachowane
console.log(decodeUriComponent('Berlin%20Office%20%ZZ%20HQ'))
// Berlin Office %ZZ HQ   ← %ZZ nie jest prawidłowym szesnastkowym — pozostawione bez zmian

// Praktyczne użycie w potoku przetwarzania logów
const rawSearchQueries = [
  'standing%20desk%20ergonomic',
  'price%3A%5B200+TO+800%5D',
  '50%25+off',       // ← z decodeURIComponent zgłosiłoby (sam %)
  'wireless%20keyboard%20%26%20mouse',
]

const decoded = rawSearchQueries.map(q => decodeUriComponent(q))
console.log(decoded)
// ['standing desk ergonomic', 'price:[200+TO+800]', '50%25+off', 'wireless keyboard & mouse']

Używaj decodeURIComponent() z try/catch dla kodu aplikacji gdzie chcesz wiedzieć natychmiast gdy pojawi się nieoczekiwane dane wejściowe. Sięgaj po decode-uri-component w potokach danych, procesorach logów i obsługujących webhooki gdzie chcesz kontynuować przetwarzanie nawet gdy niektóre dane wejściowe zawierają nieprawidłowe kodowanie.

Obsługa Zniekształconych Ciągów Zakodowanych Procentowo

Sam znak %, niepełna sekwencja jak %A lub nieprawidłowa para jak %GH powodują, że decodeURIComponent() zgłasza URIError: URI malformed. Każde dane wejściowe kontrolowane przez użytkownika — zapytania wyszukiwania, fragmenty URL, pola formularzy zawierające URL, parametry z linków kampanii emailowych — mogą zawierać te sekwencje. Bezpieczny wrapper jest niezbędny dla każdego kodu dostępnego z zewnątrz.

Bezpieczne wrappery dekodowania dla typowych scenariuszy

JavaScript
// ── 1. Podstawowe bezpieczne dekodowanie — zwraca oryginalny ciąg przy błędzie ──
function safeDecode(encoded: string): string {
  try {
    return decodeURIComponent(encoded)
  } catch {
    return encoded  // zwróć surowe wejście jeśli dekodowanie nie powiedzie się — nigdy nie zawiesza
  }
}

// ── 2. Bezpieczne dekodowanie + obsługa + jako spacji (dla wartości zakodowanych formularzami) ──
function safeFormDecode(formEncoded: string): string {
  try {
    return decodeURIComponent(formEncoded.replace(/+/g, ' '))
  } catch {
    return formEncoded.replace(/+/g, ' ')  // przynajmniej zastąp + nawet jeśli reszta nie powiedzie się
  }
}

// ── 3. Bezpieczny pełny parser ciągu zapytania → zwykły obiekt ──────────
function parseQueryString(queryString: string): Record<string, string> {
  const result: Record<string, string> = {}
  try {
    const params = new URLSearchParams(queryString)
    for (const [key, value] of params) {
      result[key] = value  // URLSearchParams obsługuje wszystkie dekodowania wewnętrznie
    }
  } catch {
    // Cicho zwróć puste przy całkowicie zniekształconych danych wejściowych
  }
  return result
}

// Użycie
console.log(safeDecode('S%C3%A3o%20Paulo'))         // São Paulo
console.log(safeDecode('search%20for%2050%25+off'))  // search for 50%+off
                                                     // → właściwie w porządku; % tu oznacza %25
console.log(safeDecode('malformed%string%'))         // malformed%string%  (bez wyjątku)

console.log(safeFormDecode('standing+desk+pro'))     // standing desk pro

console.log(parseQueryString('q=hello+world&tag=node%20js&page=2'))
// { q: 'hello world', tag: 'node js', page: '2' }

Typowe Błędy

Widziałem jak te cztery wzorce powodują ciche uszkodzenia danych lub nieoczekiwane awarie w produkcji — zazwyczaj tylko gdy wartość zawiera specjalny znak, co oznacza że prześlizgują się przez testy jednostkowe i ujawniają się na rzeczywistych danych użytkowników.

Błąd 1 — Podwójne dekodowanie wartości URLSearchParams

Problem: URLSearchParams.get() zwraca już zdekodowany ciąg. Wywołanie decodeURIComponent() na nim podwójnie dekoduje wartość — zamieniając każdy pozostały % w zdekodowanym wyjściu na %25, co niszczy dane. Poprawka: używaj wartości z URLSearchParams.get() bezpośrednio — żadne dodatkowe dekodowanie nie jest potrzebne.

Before · JavaScript
After · JavaScript
// ❌ URLSearchParams już zdekodował — dekodowanie ponownie niszczy wartości z %
const qs        = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rawRate   = qs.get('rate')       // '50%'   ← już zdekodowany
const wrongRate = decodeURIComponent(rawRate)
// '50%25'  ← % był zdekodowany do %25 w drugim przejściu — teraz znowu błędny
// ✅ Używaj URLSearchParams.get() bezpośrednio — dekodowanie jest automatyczne
const qs       = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rate     = qs.get('rate')        // '50%'   ← poprawne, bez dodatkowego kroku
const redirect = qs.get('redirect')    // 'https://dashboard.internal/'
const parsed   = new URL(redirect!)    // Bezpieczne do skonstruowania — już zdekodowane
console.log(parsed.hostname)           // dashboard.internal

Błąd 2 — Nie dekodowanie + jako spacji dla danych zakodowanych formularzami

Problem: Zgłoszenia formularzy i niektóre biblioteki OAuth kodują spacje jako + (application/x-www-form-urlencoded). Wywołanie decodeURIComponent() na tych danych pozostawia + jako dosłowny znak plus. Poprawka: używaj URLSearchParams do parsowania danych zakodowanych formularzami lub zastąp + spacją przed wywołaniem decodeURIComponent().

Before · JavaScript
After · JavaScript
// ❌ decodeURIComponent traktuje + jako dosłowny plus, nie spację
// Endpoint tokenu OAuth wysyła: grant_type=authorization_code&code=SplxlOBeZQQYb...
//   &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
// Niektóre starsze implementacje OAuth używają też + dla spacji w kodach
const formBody   = 'customer_name=Piotr+Kowalski&product=Standing+Desk+Pro&qty=2'
const [, rawVal] = formBody.split('&')[0].split('=')
const name       = decodeURIComponent(rawVal)
console.log(name)  // 'Piotr+Kowalski'  ← + nie zamieniony na spację
// ✅ Użyj URLSearchParams — przestrzega specyfikacji application/x-www-form-urlencoded
const formBody   = 'customer_name=Piotr+Kowalski&product=Standing+Desk+Pro&qty=2'
const params     = new URLSearchParams(formBody)
console.log(params.get('customer_name'))  // 'Piotr Kowalski'   ← + poprawnie zdekodowany jako spacja
console.log(params.get('product'))        // 'Standing Desk Pro'

Błąd 3 — Używanie decodeURI() dla poszczególnych wartości parametrów zapytania

Problem: decodeURI() nie dekoduje %26 (&), %3D (=) ani %3F (?) — znaków powszechnie kodowanych wewnątrz wartości parametrów. Używanie go do zdekodowania pojedynczej wartości pozostawia te sekwencje nienaruszone, po cichu produkując niepoprawne wyjście. Poprawka: używaj decodeURIComponent() dla poszczególnych wartości; zachowaj decodeURI() dla pełnych ciągów URL.

Before · JavaScript
After · JavaScript
// ❌ decodeURI nie dekoduje & i = — wartość pozostaje uszkodzona
const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'
const filter        = decodeURI(encodedFilter)
console.log(filter)
// 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'  ← %3D i %26 nie zdekodowane
// ✅ decodeURIComponent dekoduje wszystkie sekwencje włącznie z & i =
const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'
const filter        = decodeURIComponent(encodedFilter)
console.log(filter)
// 'status=active&tier=premium&region=eu-west'  ← poprawnie zdekodowane

Błąd 4 — Nie owijanie decodeURIComponent w try/catch dla danych użytkownika

Problem: Sam znak %nie poprzedzony dwoma cyframi szesnastkowymi — powszechny w zapytaniach wyszukiwania wpisanych przez użytkowników (“50% zniżki”, “100% bawełna”) — powoduje że decodeURIComponent() zgłasza URIError: URI malformed, zawieszając obsługę żądania jeśli nie jest przechwycony. Poprawka: zawsze owijaj w try/catch gdy dane wejściowe pochodzą od użytkownika, fragmentów URL lub zewnętrznych systemów.

Before · JavaScript
After · JavaScript
// ❌ Użytkownik wpisał '100% bawełna' w polu wyszukiwania — sam % zawiesza serwer
// GET /api/search?q=100%25+cotton  ← Ten konkretny przypadek jest w porządku (%25 = %)
// GET /api/search?q=100%+cotton    ← To zawiesza (% nie poprzedzony 2 cyframi szesnastkowymi)
app.get('/api/search', (req, res) => {
  const query = decodeURIComponent(req.query.q as string)
  // ↑ zgłasza URIError: URI malformed dla '100% bawełna'  → nieobsłużony błąd 500
})
// ✅ Owiń w try/catch — wróć do surowego wejścia jeśli dekodowanie się nie powiedzie
app.get('/api/search', (req, res) => {
  let query: string
  try {
    query = decodeURIComponent(req.query.q as string)
  } catch {
    query = req.query.q as string  // użyj surowej wartości zamiast zawieszać
  }
  // kontynuuj przetwarzanie bezpiecznie — query jest zdekodowane lub surowe
})

decodeURIComponent vs decodeURI vs URLSearchParams — Szybkie Porównanie

MetodaDekoduje %XXDekoduje + jako spacjęZgłasza przy błędzieDekoduje & = ? #Przypadek użyciaWymaga instalacji
decodeURIComponent()✅ wszystkie❌ nie✅ URIError✅ takPojedyncze wartości i segmenty ścieżkiNo
decodeURI()✅ większość❌ nie✅ URIError❌ niePełne ciągi URLNo
URLSearchParams✅ wszystkie✅ tak❌ cicho✅ takParsowanie ciągu zapytania z automatycznym dekodowaniemNo
Konstruktor URL✅ wszystkie✅ tak✅ TypeError✅ takPełne parsowanie i normalizacja URLNo
decode-uri-component✅ wszystkie❌ nie❌ cicho✅ takWsadowe dekodowanie z tolerancją zniekształconych danychnpm install
querystring.unescape()✅ wszystkie❌ nie❌ cicho✅ takStary Node.js (przestarzałe w v16)No (wbudowane)

W zdecydowanej większości przypadków wybór sprowadza się do trzech scenariuszy. Używaj URLSearchParams do parsowania ciągów zapytania — obsługuje dekodowanie, regułę +-jako-spacja i powtarzające się klucze automatycznie. Używaj decodeURIComponent() (owinięty w try/catch) dla pojedynczej wartości lub segmentu ścieżki, szczególnie gdy spodziewasz się ukośników zakodowanych jako %2F wewnątrz segmentu. Używaj decodeURI() tylko gdy masz pełny ciąg URL ze znakami spoza ASCII w ścieżce i potrzebujesz aby znaki strukturalne (/ ? & =) pozostały zakodowane w wyjściu.

Często Zadawane Pytania

Jaka jest różnica między decodeURIComponent() i decodeURI() w JavaScript?
decodeURIComponent() dekoduje każdą sekwencję zakodowaną procentowo w ciągu, w tym znaki mające strukturalne znaczenie w URL — & (%26), = (%3D), ? (%3F), # (%23), / (%2F) i : (%3A). Jest zaprojektowany do dekodowania poszczególnych wartości parametrów zapytania lub segmentów ścieżki. decodeURI() zachowuje te strukturalne znaki — nie dekoduje %26, %3D, %3F, %23, %2F ani %3A — ponieważ jest zaprojektowany do sanityzacji pełnego URL bez niszczenia struktury ciągu zapytania. Używanie decodeURI() na pojedynczej wartości parametru zawierającej zakodowane & lub = po cichu pozostawi te sekwencje niezakodowane, produkując błędne wyjście.
Dlaczego decodeURIComponent() zgłasza URIError dla niektórych ciągów?
decodeURIComponent() zgłasza URIError: URI malformed gdy dane wejściowe zawierają % nie poprzedzony dokładnie dwoma prawidłowymi cyframi szesnastkowymi. Typowe przyczyny: sam % na końcu ciągu ("50% zniżki" wpisane przez użytkownika), niepełna sekwencja ("%A") lub para nieszesnastkowa ("%GH"). Zdarza się to najczęściej z zapytaniami wyszukiwania wpisanymi przez użytkownika lub wartościami wklejonymi z tekstu, który nigdy nie miał być zakodowany w URL. Poprawka polega na owinięciu decodeURIComponent() blokiem try/catch i zwróceniu surowego ciągu przy błędzie. Pakiet npm decode-uri-component oferuje taką samą poprawkę bez wymagania wrappera try/catch.
Czy URLSearchParams automatycznie dekoduje wartości zakodowane procentowo?
Tak. Każda wartość zwrócona przez URLSearchParams.get() i URLSearchParams.getAll() jest w pełni zdekodowana — nigdy nie powinieneś wywoływać decodeURIComponent() na ich wyjściu. URLSearchParams przestrzega też specyfikacji application/x-www-form-urlencoded, która dekoduje + jako spację, co czyni ją poprawną dla zgłoszeń formularzy HTML i odpowiedzi tokenów OAuth. Jedynym przypadkiem gdy URLSearchParams nie może pomóc jest dekodowanie samodzielnej zakodowanej wartości która nie jest częścią ciągu zapytania — w tym celu używaj decodeURIComponent() z try/catch.
Jak zdekodować znak + jako spację w JavaScript?
decodeURIComponent() traktuje + jako dosłowny znak plus — nie konwertuje + na spację. Jest to celowe: + jako spacja jest konwencją application/x-www-form-urlencoded, odrębną od standardu kodowania procentowego. Aby zdekodować + jako spację, użyj URLSearchParams (który przestrzega specyfikacji zakodowanych formularzami), lub zastąp + przed wywołaniem decodeURIComponent(): decodeURIComponent(str.replace(/\+/g, ' ')). Pamiętaj, że zastąpienie + przez %20 przed dekodowaniem też działa, ale jest nieco bardziej rozwlekłe. Zawsze preferuj URLSearchParams do parsowania pełnych ciągów zapytania — poprawnie obsługuje zarówno %20 jak i +.
Jak zdekodować ciąg URL w Node.js?
Używaj tych samych globalnych funkcji dostępnych we wszystkich przeglądarkach — w Node.js 10+ nie jest wymagany żaden import. decodeURIComponent() dla pojedynczych wartości, decodeURI() dla pełnych ciągów URL, URLSearchParams dla ciągów zapytania. Dla przychodzących URL żądań HTTP, używaj new URL(req.url, 'http://localhost') — konstruktor URL normalizuje URL i udostępnia .searchParams (automatycznie zdekodowane) i .pathname (zdekodowane na poziomie URL). Starsza funkcja querystring.unescape() nadal istnieje, ale jest przestarzała od Node.js 16 — zamiast niej preferuj decodeURIComponent().
Jak sparsować ciąg zapytania URL do obiektu w JavaScript?
Użyj URLSearchParams z Object.fromEntries(): const params = Object.fromEntries(new URLSearchParams(queryString)). To daje zwykły obiekt z wszystkimi kluczami parametrów i zdekodowanymi wartościami. Pamiętaj, że Object.fromEntries() zachowuje tylko ostatnią wartość dla zduplikowanych kluczy — dla powtarzających się kluczy jak tag=one&tag=two, używaj URLSearchParams.getAll('tag') zamiast tego. W przeglądarce, new URLSearchParams(window.location.search) automatycznie parsuje ciąg zapytania bieżącej strony. W Node.js, parsuj z new URL(req.url, base).searchParams.

Powiązane Narzędzia

Aby jednym kliknięciem zdekodować bez pisania kodu, wklej swój ciąg zakodowany procentowo bezpośrednio do Dekodera URL ToolDeck — dekoduje natychmiast w przeglądarce, z wynikiem gotowym do skopiowania do kodu lub terminala.

AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

MW
Marcus WebbRecenzent techniczny

Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.