URL Decode JavaScript — decodeURIComponent()
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.
// 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
// 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
// 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')) // platformDecodificação de segmentos de caminho não ASCII e Unicode
// 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)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:
| Codificado | Caractere | decodeURIComponent() | decodeURI() | URLSearchParams |
|---|---|---|---|---|
| %20 | espaço | espaç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
// 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
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
// 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=pendingAnálise de parâmetros de consulta da URL atual do navegador
// 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 furnitureDecodificando 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
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
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
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.
# ── 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.
npm install decode-uri-component # ou pnpm add decode-uri-component
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
// ── 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.
// ❌ 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.internalErro 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().
// ❌ 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.
// ❌ 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®ion=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.
// ❌ 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étodo | Decodifica %XX | Decodifica + como espaço | Lança com malformado | Decodifica & = ? # | Caso de uso | Requer instalação |
|---|---|---|---|---|---|---|
| decodeURIComponent() | ✅ todos | ❌ não | ✅ URIError | ✅ sim | Valores individuais e segmentos de caminho | Não |
| decodeURI() | ✅ maioria | ❌ não | ✅ URIError | ❌ não | Strings de URL completas | Não |
| URLSearchParams | ✅ todos | ✅ sim | ❌ silencioso | ✅ sim | Análise de query strings com decodificação automática | Não |
| Construtor URL | ✅ todos | ✅ sim | ✅ TypeError | ✅ sim | Análise e normalização de URLs completas | Não |
| decode-uri-component | ✅ todos | ❌ não | ❌ silencioso | ✅ sim | Decodificação em lote com tolerância a entrada malformada | npm install |
| querystring.unescape() | ✅ todos | ❌ não | ❌ silencioso | ✅ sim | Node.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
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.
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.
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.