URL Decode JavaScript — decodeURIComponent()

·Front-end & Node.js Developer·Revisado porMarcus Webb·Publicado

Use o Decodificador de URL Online gratuito diretamente no seu navegador — sem instalação.

Experimentar Decodificador de URL Online online →

Strings codificadas por porcentagem aparecem constantemente no código JavaScript — uma consulta de busca chega como q=standing+desk%26price%3A200, um redirecionamento OAuth como next=https%3A%2F%2Fdashboard.internal%2F, um caminho de armazenamento como reports%2F2025%2Fq1.pdf. Como fazer URL decode em JavaScript se resume a escolher a função integrada correta entre três opções: decodeURIComponent(), decodeURI() e URLSearchParams — e a escolha entre elas é a causa raiz da maioria das corrupções silenciosas de dados que vi em bases de código de produção, especialmente o caso limite do + como espaço e a dupla decodificação. Para uma decodificação rápida sem escrever código, o Decodificador de URL da ToolDeck faz isso instantaneamente no navegador. Este tutorial de decodificação de URL em JavaScript cobre as três funções em profundidade (ES2015+ / Node.js 10+): quando usar cada uma, como diferem para espaços e caracteres reservados, decodificação de arquivos e requisições HTTP, tratamento seguro de erros e os quatro erros que causam os bugs de produção mais sutis.

  • decodeURIComponent() decodifica todas as sequências codificadas por porcentagem — é a escolha certa para valores de parâmetros de consulta individuais e segmentos de caminho
  • decodeURI() preserva os caracteres estruturais da URI como / ? & = # : — use-o apenas ao decodificar uma string de URL completa, nunca para valores individuais
  • URLSearchParams.get() retorna valores já decodificados — chamar decodeURIComponent() sobre o resultado causa dupla decodificação
  • decodeURIComponent() NÃO decodifica + como espaço — para dados codificados como formulário (application/x-www-form-urlencoded), use URLSearchParams ou substitua + antes de decodificar
  • Sempre envolva decodeURIComponent() em try/catch quando a entrada vier de dados do usuário — um % isolado ou sequência incompleta lança URIError

O que é decodificação de URL?

A codificação por porcentagem (formalmente definida na RFC 3986) substitui os caracteres que são inseguros ou estruturalmente significativos em uma URL por um sinal % seguido de dois dígitos hexadecimais — o valor em bytes UTF-8 do caractere. A decodificação de URL inverte essa transformação: cada sequência %XX é convertida de volta ao seu byte original, e a sequência de bytes resultante é interpretada como texto UTF-8. Um espaço decodificado de %20, uma barra de %2F, o caractere não ASCII ü de %C3%BC (sua representação UTF-8 de dois bytes).

Os caracteres que nunca são codificados — chamados de caracteres não reservados — são as letras A–Z e a–z, os dígitos 0–9 e - _ . ~. Todo o resto tem um papel estrutural na URL (como / separando segmentos de caminho ou & separando parâmetros de consulta) ou deve ser codificado quando usado como dado. O resultado prático: um filtro de busca como status=active&tier=premium codificado como valor de um único parâmetro de consulta chega sem nenhuma semelhança com o original.

Before · text
After · text
// Codificado por porcentagem — como recebido em uma requisição HTTP ou payload de webhook
"q=price%3A%5B200+TO+800%5D%20AND%20brand%3ANorthwood%20%26%20status%3Ain-stock"
// Após a decodificação de URL — o filtro original do Elasticsearch
"q=price:[200 TO 800] AND brand:Northwood & status:in-stock"

decodeURIComponent() — A função padrão para decodificar valores

decodeURIComponent() é o motor da decodificação de URL em JavaScript. Decodifica cada sequência %XX na string de entrada — incluindo caracteres que têm significado estrutural em uma URL, como %2F (barra), %3F (interrogação), %26 (e comercial) e %3D (sinal de igual). Isso a torna a escolha correta para decodificar valores de parâmetros de consulta individuais e segmentos de caminho, mas a escolha errada para decodificar uma URL completa — onde esses caracteres estruturais devem permanecer codificados. É uma função global: nenhuma importação é necessária em nenhum ambiente JavaScript.

Exemplo mínimo funcional

JavaScript (browser / Node.js)
// Decodificar valores de parâmetros de consulta individuais — o caso mais comum

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]'
// Nota: + NÃO é decodificado como espaço — ver a seção + na tabela de comparação

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

Decodificação de uma URL de redirecionamento extraída de um parâmetro de consulta

JavaScript
// A URL de redirecionamento foi codificada por porcentagem ao ser incorporada como valor de parâmetro
// lado receptor: extraí-la e usá-la — sem decodificação manual com 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')       // Decodificado automaticamente por URLSearchParams
const sessionId = url.searchParams.get('session_id') // 'sid_7x9p2k'

// rawNext já está decodificado: 'https://dashboard.internal/reports?view=weekly&team=platform'
// NÃO chamar decodeURIComponent(rawNext) novamente — isso seria dupla decodificação

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

Decodificação de segmentos de caminho não ASCII e Unicode

JavaScript
// API REST com segmentos de caminho internacionalizados
// Cada byte UTF-8 do caractere original foi codificado por porcentagem separadamente

const encodedSegments = [
  '%E6%9D%B1%E4%BA%AC',   // 東京  (Tokyo)   — 3 bytes por caractere
  'M%C3%BCnchen',         // München          — ü codificada como 2 bytes
  'caf%C3%A9',            // café             — é como NFC pré-composto
  'S%C3%A3o%20Paulo',     // São Paulo
]

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

// Extraindo uma chave de arquivo de uma URL de API de armazenamento
// A chave do objeto continha / por isso foi codificada como %2F dentro do segmento de caminho
const storageUrl = 'https://storage.api.example.com/v1/objects/reports%2F2025%2Fq1-financials.pdf'
const rawKey     = new URL(storageUrl).pathname.replace('/v1/objects/', '')
// .pathname decodifica a codificação no nível URL mas %2F (como %252F no nível URL) permanece
// Usar decodeURIComponent para o passo final:
const fileKey = decodeURIComponent(rawKey)  // 'reports/2025/q1-financials.pdf'
console.log(fileKey)
Nota:decodeURIComponent() lança um URIError quando a entrada contém um % não seguido de dois dígitos hexadecimais válidos — por exemplo um % isolado no final de uma string ou uma sequência como %GH. Sempre o envolva em try/catch ao decodificar entrada fornecida pelo usuário. Os padrões de decodificação segura são abordados na seção de Tratamento de Erros abaixo.

Funções de decodificação de URL em JavaScript — Referência de caracteres

As três funções de decodificação integradas diferem exatamente em quais sequências codificadas decodificam. A tabela mostra o comportamento para os caracteres que mais importam na prática:

CodificadoCaracteredecodeURIComponent()decodeURI()URLSearchParams
%20espaçoespaço ✅espaço ✅espaço ✅
+mais (formulário)+ (mantido)+ (mantido)espaço ✅
%2B+ literal+ ✅+ ✅+ ✅
%26&& ✅& (mantido) ❌& ✅
%3D== ✅= (mantido) ❌= ✅
%3F?? ✅? (mantido) ❌? ✅
%23## ✅# (mantido) ❌# ✅
%2F// ✅/ (mantido) ❌/ ✅
%3A:: ✅: (mantido) ❌: ✅
%40@@ ✅@ (mantido) ❌@ ✅
%25%% ✅% ✅% ✅
%C3%BCüü ✅ü ✅ü ✅

As duas linhas críticas são + e os caracteres estruturais (%26, %3D, %3F). URLSearchParams decodifica + como espaço porque segue a especificação application/x-www-form-urlencoded — correto para envios de formulários HTML, mas diferente do que decodeURIComponent() faz com o mesmo caractere. E decodeURI() silenciosamente ignora %26, %3D e %3F — o que parece correto até que um valor realmente contenha um e comercial ou sinal de igual codificado.

decodeURI() — Decodificando uma URL completa sem quebrar sua estrutura

decodeURI() é o complemento de encodeURI(). Decodifica uma string de URL completa preservando os caracteres que têm significado estrutural em uma URI: ; , / ? : @ & = + $ #. Estes são deixados em sua forma codificada por porcentagem (ou como caracteres literais se apareceram não codificados na entrada). Isso torna decodeURI()seguro para limpar URLs completas que podem conter caracteres não ASCII no caminho ou nome de host, sem colapsar acidentalmente a estrutura da query string.

Limpeza de uma URL com segmentos de caminho não ASCII

JavaScript
// Uma URL de CDN com segmentos de caminho internacionalizados e uma query string estruturada
// decodeURI() decodifica o caminho não ASCII mas preserva ? & = intactos

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
// ↑ Não ASCII decodificado; ? & = preservados — URL permanece estruturalmente válida

// decodeURIComponent destruiria a URL — : / ? & = todos decodificados de uma vez
const broken = decodeURIComponent(encodedUrl)
// 'https://cdn.example.com/assets/東京/2025/q1-report.pdf?token=eyJ0eXAiOiJKV1QiLCJhbGci&expires=1735689600'
// Parece igual aqui, mas uma URL como 'https%3A%2F%2F...' seria destruída
Nota:Prefira o construtor URL em vez de decodeURI() quando também precisar acessar componentes individuais da URL. new URL(str) normaliza a entrada, valida sua estrutura e expõe .pathname, .searchParams e .hostname como propriedades já decodificadas. Reserve decodeURI() para casos onde você só precisa de um resultado em string e não pode usar o construtor URL (por exemplo, em ambientes muito antigos do Node.js 6 sem a classe global URL).

URLSearchParams — Decodificação automática para query strings

URLSearchParams é a forma idiomática de analisar query strings em JavaScript moderno. Cada valor retornado por .get(), .getAll() ou iteração é automaticamente decodificado — incluindo + como espaço para dados codificados como formulário. Está disponível globalmente em todos os navegadores modernos e Node.js 10+, não requer importação e lida com os casos extremos que a divisão manual de strings erra.

Análise de uma query string recebida

JavaScript (browser / Node.js 10+)
// Analisando uma query string de callback de webhook ou redirecionamento 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'       ← + decodificado como espaço
console.log(params.get('filter'))        // 'price:[200+TO+800]'      ← %3A e %5B decodificados
console.log(params.getAll('tag'))        // ['ergonomic', 'adjustable']
console.log(params.get('redirect'))      // 'https://dashboard.internal/orders?view=pending'

// Iterando todos os parâmetros
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

Análise de parâmetros de consulta da URL atual do navegador

JavaScript (browser)
// URL atual: 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   (+ decodificado automaticamente)
console.log(filters.category)  // office furniture

Decodificando dados codificados por URL de arquivos e respostas de API

Dois cenários aparecem constantemente em projetos reais: processar um arquivo em disco que contém dados codificados por porcentagem (logs de acesso, exportações de dados, arquivos de captura de webhooks) e analisar a URL de uma requisição HTTP de entrada em um servidor Node.js. Ambos seguem o mesmo princípio — use o construtor URL ou URLSearchParams em vez de divisão manual de strings — mas os detalhes diferem.

Lendo e decodificando registros codificados por URL de um arquivo

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

// Arquivo: orders-export.txt — um registro codificado por URL por linha
// customer_name=Jo%C3%A3o+Silva&order_id=ord_9c2f4a&product=Standing+Desk+Pro&total=149.99%20EUR
// customer_name=Camila+Rocha&order_id=ord_7b3a1c&product=Ergonomic+Chair&total=89.00%20EUR
// customer_name=Ana+Oliveira&order_id=ord_2e8d5f&product=Monitor+Arm&total=59.00%20EUR

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 decodifica + como espaço e sequências %XX automaticamente
    const params = new URLSearchParams(line)

    orders.push({
      customerName: params.get('customer_name'),  // 'João Silva'
      orderId:      params.get('order_id'),        // 'ord_9c2f4a'
      product:      params.get('product'),         // 'Standing Desk Pro'
      total:        params.get('total'),           // '149.99 EUR'
    })
  }

  return orders
}

const orders = await parseOrdersFile('./orders-export.txt')
console.log(orders[0])
// { customerName: 'João Silva', orderId: 'ord_9c2f4a', product: 'Standing Desk Pro', total: '149.99 EUR' }

Análise de um log de acesso Nginx para decodificar consultas de busca

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

// As linhas do log de acesso do Nginx têm este aspecto:
// 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) {
    // Extrair o caminho da requisição da linha do log
    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 decodifica automaticamente
    } catch {
      // Ignorar linhas malformadas — logs de acesso podem conter entradas truncadas
    }
  }

  return queries
}

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

Análise de parâmetros de consulta em um servidor HTTP Node.js

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

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

const server = http.createServer((req, res) => {
  // Construir uma URL completa — o segundo argumento é a base exigida pelo construtor URL
  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)

Quando precisar inspecionar uma URL codificada durante o desenvolvimento — para entender o que um webhook está enviando antes de escrever o código de análise — cole-a diretamente no Decodificador de URL da ToolDeck para ver a forma decodificada instantaneamente sem executar um script.

Decodificação de URL pela linha de comando

Para scripts shell, pipelines de CI ou inspeção rápida de strings codificadas, várias abordagens funcionam sem escrever um script completo. Os one-liners do Node.js são multiplataforma; no macOS e Linux, python3 também está sempre disponível.

bash
# ── One-liners do Node.js ──────────────────────────────────────────────────

# Decodificar um único valor codificado por porcentagem
node -e "console.log(decodeURIComponent(process.argv[1]))" "S%C3%A3o%20Paulo%20%26%20Rio"
# São Paulo & Rio

# Analisar uma query string e imprimir cada par chave=valor (decodificado)
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

# Decodificar e exibir formatado um corpo JSON codificado por URL (comum na depuração de webhooks)
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"
# }

# ── One-liner Python (disponível na maioria dos sistemas macOS/Linux) ─────────────
# unquote_plus também decodifica + como espaço — correto para dados codificados como formulário
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 — registrar e decodificar uma URL de redirecionamento de um cabeçalho de resposta ──────────
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))
  "

Decodificação tolerante com decode-uri-component

O decodeURIComponent() integrado lança um URIError em qualquer sequência malformada — incluindo um % final sem dois dígitos hexadecimais seguintes. Em código de produção que processa URLs fornecidas pelo usuário ou de terceiros — logs de consultas de busca, URLs de cliques de campanhas de e-mail, dados web raspados — sequências malformadas são comuns o suficiente para causar falhas. O pacote decode-uri-component (~30M de downloads semanais no npm) lida com sequências malformadas de forma tolerante, retornando a sequência original sem alterações em vez de lançar um erro.

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

// A função nativa lança erro com entrada malformada — pode derrubar um handler de requisição
try {
  decodeURIComponent('product%name%')   // ❌ URIError: URI malformed
} catch (e) {
  console.error('Nativa lançou:', (e as Error).message)
}

// decode-uri-component retorna a sequência raw em vez de lançar
console.log(decodeUriComponent('product%name%'))
// product%name%   ← sequências malformadas deixadas como estão, sem lançar erro

// Sequências válidas são decodificadas corretamente
console.log(decodeUriComponent('S%C3%A3o%20Paulo%20%26%20Rio'))
// São Paulo & Rio

// Misto: sequências válidas decodificadas, inválidas preservadas
console.log(decodeUriComponent('Berlin%20Office%20%ZZ%20HQ'))
// Berlin Office %ZZ HQ   ← %ZZ não é hex válido — mantido como está

// Uso prático em um pipeline de processamento de logs
const rawSearchQueries = [
  'standing%20desk%20ergonomic',
  'price%3A%5B200+TO+800%5D',
  '50%25+off',       // ← lançaria erro com decodeURIComponent  (% isolado)
  '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']

Use decodeURIComponent() com try/catch no código de aplicação onde você quer saber imediatamente se entrada inesperada chegar. Recorra a decode-uri-component em pipelines de dados, processadores de logs e handlers de webhooks onde você quer continuar processando mesmo quando algumas entradas contêm codificação inválida.

Tratando strings codificadas por porcentagem malformadas

Um % isolado, uma sequência incompleta como %A ou um par inválido como %GH fazem com que decodeURIComponent() lance URIError: URI malformed. Qualquer entrada controlada pelo usuário — consultas de busca, fragmentos de URL, campos de formulário contendo URLs, parâmetros de links de campanhas de e-mail — pode conter essas sequências. Um wrapper seguro é essencial para qualquer código exposto externamente.

Wrappers de decodificação segura para cenários comuns

JavaScript
// ── 1. Decodificação segura básica — retorna string original em caso de erro ─────────────
function safeDecode(encoded: string): string {
  try {
    return decodeURIComponent(encoded)
  } catch {
    return encoded  // retornar input raw se a decodificação falhar — nunca travar
  }
}

// ── 2. Decodificação segura + tratamento de + como espaço (para valores codificados como formulário) ─────────
function safeFormDecode(formEncoded: string): string {
  try {
    return decodeURIComponent(formEncoded.replace(/+/g, ' '))
  } catch {
    return formEncoded.replace(/+/g, ' ')  // pelo menos substituir + mesmo que o resto falhe
  }
}

// ── 3. Parser seguro de query string completa → objeto simples ──────────────────────
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 lida com toda a decodificação internamente
    }
  } catch {
    // Retornar vazio silenciosamente para entrada completamente malformada
  }
  return result
}

// Uso
console.log(safeDecode('S%C3%A3o%20Paulo'))         // São Paulo
console.log(safeDecode('search%20for%2050%25+off'))  // search for 50% off (% isolado)
                                                     // → na verdade está correto; % aqui é %25
console.log(safeDecode('malformed%string%'))         // malformed%string%  (sem lançar erro)

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' }

Erros comuns

Vi esses quatro padrões causar corrupção silenciosa de dados ou falhas inesperadas em produção — geralmente apenas quando um valor contém um caractere especial, o que significa que passam pelos testes unitários e surgem com dados reais de usuários.

Erro 1 — Dupla decodificação de um valor URLSearchParams

Problema: URLSearchParams.get() retorna uma string já decodificada. Chamar decodeURIComponent() sobre o resultado decodifica o valor duas vezes — convertendo qualquer % restante na saída decodificada em %25, corrompendo os dados. Correção: use o valor de URLSearchParams.get() diretamente — nenhuma decodificação adicional é necessária.

Before · JavaScript
After · JavaScript
// ❌ URLSearchParams já decodificou — decodificar novamente corrompe valores com %
const qs        = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rawRate   = qs.get('rate')       // '50%'   ← já decodificado
const wrongRate = decodeURIComponent(rawRate)
// '50%25'  ← % foi decodificado para %25 na segunda passagem — agora errado novamente
// ✅ Usar URLSearchParams.get() diretamente — a decodificação é automática
const qs       = new URLSearchParams('rate=50%25&redirect=https%3A%2F%2Fdashboard.internal%2F')
const rate     = qs.get('rate')        // '50%'   ← correto, nenhum passo extra necessário
const redirect = qs.get('redirect')    // 'https://dashboard.internal/'
const parsed   = new URL(redirect!)    // Seguro para construir — já decodificado
console.log(parsed.hostname)           // dashboard.internal

Erro 2 — Não decodificar + como espaço para dados codificados como formulário

Problema: Envios de formulários e algumas bibliotecas OAuth codificam espaços como + (application/x-www-form-urlencoded). Chamar decodeURIComponent() nesses dados deixa + como um sinal de mais literal. Correção: use URLSearchParams para analisar dados codificados como formulário, ou substitua + por espaço antes de chamar decodeURIComponent().

Before · JavaScript
After · JavaScript
// ❌ decodeURIComponent trata + como sinal de mais literal, não como espaço
// O endpoint de token OAuth envia: grant_type=authorization_code&code=SplxlOBeZQQYb...
//   &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
// Algumas implementações OAuth legadas também usam + para espaços em códigos
const formBody   = 'customer_name=Jo%C3%A3o+Silva&product=Standing+Desk+Pro&qty=2'
const [, rawVal] = formBody.split('&')[0].split('=')
const name       = decodeURIComponent(rawVal)
console.log(name)  // 'Jo%C3%A3o+Silva'  ← + não convertido para espaço
// ✅ Usar URLSearchParams — segue a especificação application/x-www-form-urlencoded
const formBody   = 'customer_name=Jo%C3%A3o+Silva&product=Standing+Desk+Pro&qty=2'
const params     = new URLSearchParams(formBody)
console.log(params.get('customer_name'))  // 'João Silva'   ← + corretamente decodificado como espaço
console.log(params.get('product'))        // 'Standing Desk Pro'

Erro 3 — Usar decodeURI() para valores de parâmetros de consulta individuais

Problema: decodeURI() não decodifica %26 (&), %3D (=) nem %3F (?) — caracteres comumente codificados dentro de valores de parâmetros. Usá-lo para decodificar um único valor deixa essas sequências intactas, produzindo silenciosamente uma saída incorreta. Correção: use decodeURIComponent() para valores individuais; reserve decodeURI() para strings de URL completas.

Before · JavaScript
After · JavaScript
// ❌ decodeURI não decodifica & e = — o valor continua quebrado
const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'
const filter        = decodeURI(encodedFilter)
console.log(filter)
// 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'  ← %3D e %26 não decodificados
// ✅ decodeURIComponent decodifica todas as sequências incluindo & e =
const encodedFilter = 'status%3Dactive%26tier%3Dpremium%26region%3Deu-west'
const filter        = decodeURIComponent(encodedFilter)
console.log(filter)
// 'status=active&tier=premium&region=eu-west'  ← corretamente decodificado

Erro 4 — Não envolver decodeURIComponent em try/catch para entrada do usuário

Problema: Um %isolado não seguido de dois dígitos hexadecimais — comum em consultas de busca digitadas por usuários (“50% de desconto”, “100% algodão”) — faz com que decodeURIComponent() lance URIError: URI malformed, derrubando o handler de requisição se não capturado. Correção: sempre envolva em try/catch quando a entrada vem de dados do usuário, fragmentos de URL ou sistemas externos.

Before · JavaScript
After · JavaScript
// ❌ Usuário digitou '100% cotton' em uma caixa de busca — % isolado derruba o servidor
// GET /api/search?q=100%25+cotton  ← Este caso específico está correto (%25 = %)
// GET /api/search?q=100%+cotton    ← Este trava (% não seguido de 2 dígitos hex)
app.get('/api/search', (req, res) => {
  const query = decodeURIComponent(req.query.q as string)
  // ↑ lança URIError: URI malformed para '100% cotton'  → erro 500 não tratado
})
// ✅ Envolver em try/catch — recorrer à entrada raw se a decodificação falhar
app.get('/api/search', (req, res) => {
  let query: string
  try {
    query = decodeURIComponent(req.query.q as string)
  } catch {
    query = req.query.q as string  // usar o valor raw em vez de travar
  }
  // continuar processando com segurança — query está decodificada ou é raw
})

decodeURIComponent vs decodeURI vs URLSearchParams — Comparação rápida

MétodoDecodifica %XXDecodifica + como espaçoLança com malformadoDecodifica & = ? #Caso de usoRequer instalação
decodeURIComponent()✅ todos❌ não✅ URIError✅ simValores individuais e segmentos de caminhoNão
decodeURI()✅ maioria❌ não✅ URIError❌ nãoStrings de URL completasNão
URLSearchParams✅ todos✅ sim❌ silencioso✅ simAnálise de query strings com decodificação automáticaNão
Construtor URL✅ todos✅ sim✅ TypeError✅ simAnálise e normalização de URLs completasNão
decode-uri-component✅ todos❌ não❌ silencioso✅ simDecodificação em lote com tolerância a entrada malformadanpm install
querystring.unescape()✅ todos❌ não❌ silencioso✅ simNode.js legado (obsoleto desde v16)Não (integrado)

Para a grande maioria dos casos, a escolha se reduz a três cenários. Use URLSearchParams para analisar query strings — ele lida com a decodificação, a regra do + como espaço e chaves repetidas automaticamente. Use decodeURIComponent() (envolvido em try/catch) para um único valor ou segmento de caminho, especialmente quando você espera barras codificadas como %2F dentro de um segmento. Use decodeURI() apenas quando você tiver uma string de URL completa com caracteres não ASCII no caminho e precisar que os caracteres estruturais (/ ? & =) permaneçam codificados na saída.

Perguntas frequentes

Qual é a diferença entre decodeURIComponent() e decodeURI() em JavaScript?
decodeURIComponent() decodifica todas as sequências codificadas por porcentagem na string, incluindo caracteres que têm significado estrutural em uma URL — & (%26), = (%3D), ? (%3F), # (%23), / (%2F) e : (%3A). É projetada para decodificar valores de parâmetros de consulta individuais ou segmentos de caminho. decodeURI() preserva esses caracteres estruturais — não decodifica %26, %3D, %3F, %23, %2F nem %3A — porque é projetada para limpar uma URL completa sem quebrar sua estrutura de query string. Usar decodeURI() em um único valor de parâmetro que contém um & ou = codificado deixará silenciosamente essas sequências não decodificadas, produzindo saída incorreta.
Por que decodeURIComponent() lança URIError para algumas strings?
decodeURIComponent() lança URIError: URI malformed quando a entrada contém um % não seguido de exatamente dois dígitos hexadecimais válidos. Gatilhos comuns: um % isolado no final de uma string ("50% de desconto" digitado por um usuário), uma sequência incompleta ("%A") ou um par não hexadecimal ("%GH"). Isso acontece com mais frequência em consultas de busca digitadas por usuários ou valores colados de texto que nunca foi pensado para ser codificado por URL. A correção é envolver decodeURIComponent() em um bloco try/catch e retornar a string raw em caso de erro. O pacote npm decode-uri-component oferece a mesma correção sem exigir um wrapper try/catch.
URLSearchParams decodifica automaticamente os valores codificados por porcentagem?
Sim. Cada valor retornado por URLSearchParams.get() e URLSearchParams.getAll() está completamente decodificado — você nunca deve chamar decodeURIComponent() sobre sua saída. URLSearchParams também segue a especificação application/x-www-form-urlencoded, que decodifica + como espaço, tornando-o correto para envios de formulários HTML e respostas de token OAuth. O único caso em que URLSearchParams não pode ajudar é decodificar um valor codificado independente que não faz parte de uma query string — para isso, use decodeURIComponent() com try/catch.
Como decodifico o sinal + como espaço em JavaScript?
decodeURIComponent() trata + como um sinal de mais literal — não converte + em espaço. Isso é intencional: + como espaço é uma convenção do application/x-www-form-urlencoded, separada do padrão de codificação por porcentagem. Para decodificar + como espaço, use URLSearchParams (que segue a especificação de formulário codificado) ou substitua + antes de chamar decodeURIComponent(): decodeURIComponent(str.replace(/\+/g, ' ')). Note que substituir + por %20 antes de decodificar também funciona, mas é ligeiramente mais verboso. Sempre prefira URLSearchParams para analisar query strings completas — ele lida com %20 e + corretamente.
Como faço URL decode de uma string no Node.js?
Use as mesmas funções globais disponíveis em todos os navegadores — nenhuma importação é necessária no Node.js 10+. decodeURIComponent() para valores individuais, decodeURI() para strings de URL completas, URLSearchParams para query strings. Para URLs de requisições HTTP de entrada, use new URL(req.url, 'http://localhost') — o construtor URL normaliza a URL e expõe .searchParams (decodificado automaticamente) e .pathname (decodificado no nível URL). A função mais antiga querystring.unescape() ainda existe mas está obsoleta desde o Node.js 16 — prefira decodeURIComponent() em vez disso.
Como analiso uma query string de URL em um objeto em JavaScript?
Use URLSearchParams com Object.fromEntries(): const params = Object.fromEntries(new URLSearchParams(queryString)). Isso dá um objeto simples com todas as chaves de parâmetros e valores decodificados. Note que Object.fromEntries() mantém apenas o último valor para chaves duplicadas — para chaves repetidas como tag=one&tag=two, use URLSearchParams.getAll('tag') em vez disso. No navegador, new URLSearchParams(window.location.search) analisa automaticamente a query string da página atual. No Node.js, analise a partir de new URL(req.url, base).searchParams.

Ferramentas relacionadas

Para decodificar com um clique sem escrever nenhum código, cole sua string codificada por porcentagem diretamente no Decodificador de URL da ToolDeck — decodifica instantaneamente no navegador, com o resultado pronto para copiar no seu código ou terminal.

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 WebbRevisor técnico

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.