5 erros comuns de decodificação de URL e como corrigi-los
Erros de decodificação de URL podem transformar uma experiência de usuário tranquila em um pesadelo de debugging. Com base em anos de experiência em desenvolvimento web e milhares de relatórios de bugs, aqui estão os 5 erros mais comuns de decodificação de URL—e exatamente como corrigi-los.
Erro #1: Formato de Codificação Percentual Incorreto
O Problema
Nem todas as strings que parecem codificadas em URL são realmente válidas. Sequências de porcentagem inválidas causarão falha na decodificação.
Padrões inválidos comuns:
hello%world // Dígitos hexadecimais ausentes
test%2 // Sequência incompleta (precisa de 2 dígitos hex)
data%ZZ // Caracteres hexadecimais inválidos
url%GG%20test // Mistura de inválido (%GG) e válido (%20)
O Que Acontece
// Isso gerará um erro!
decodeURIComponent('hello%world');
// URIError: URI malformed
decodeURIComponent('test%2');
// URIError: URI mal formed
A Causa Raiz
- Construção manual de URL sem codificação adequada
- URLs truncadas (erros de copiar-colar)
- Dados não-URL confundidos com strings codificadas
- Sistemas legados que não seguem RFC 3986
A Solução
Correção #1: Validar antes de decodificar
function isValidEncoded(str) {
// Verificar padrões de porcentagem inválidos
const invalidPattern = /%(?![0-9A-Fa-f]{2})|%[0-9A-Fa-f](?![0-9A-Fa-f])/;
if (invalidPattern.test(str)) {
return false;
}
// Tentar decodificar - se lançar erro, é inválido
try {
decodeURIComponent(str);
return true;
} catch (e) {
return false;
}
}
// Uso
const userInput = params.get('search');
if (isValidEncoded(userInput)) {
const decoded = decodeURIComponent(userInput);
} else {
console.error('Codificação de URL inválida detectada');
// Lidar com o erro apropriadamente
}
Correção #2: Sanitizar codificações malformadas
function sanitizeEncoding(str) {
// Substituir sequências de porcentagem incompletas ou inválidas
return str.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
// Converte % para %25 quando não seguido por 2 dígitos hex
}
// Exemplo
sanitizeEncoding('hello%world'); // → 'hello%25world'
decodeURIComponent(sanitizeEncoding('hello%world')); // → 'hello%world'
Correção #3: Pré-processar com regex
function safelyDecode(str) {
try {
return decodeURIComponent(str);
} catch (e) {
// Fallback: substituir padrões comuns manualmente
return str
.replace(/%20/g, ' ')
.replace(/%21/g, '!')
.replace(/%40/g, '@')
.replace(/%23/g, '#')
.replace(/%25/g, '%');
// Nota: Isso não é abrangente, apenas um fallback
}
}
Prevenção
Sempre use funções de codificação adequadas:
// ✅ Correto
const query = encodeURIComponent(userInput);
const url = `/search?q=${query}`;
// ❌ Errado - construção manual de URL
const url = `/search?q=${userInput.replace(/ /g, '%20')}`;
Teste Rápido
// Casos de teste para validação
const testCases = [
{ input: 'hello%20world', valid: true },
{ input: 'hello%world', valid: false },
{ input: 'test%2', valid: false },
{ input: '%E4%B8%AD%E6%96%87', valid: true },
{ input: 'normal-text', valid: true }, // Sem codificação também é válido
];
testCases.forEach(({ input, valid }) => {
const result = isValidEncoded(input);
console.assert(result === valid, `Falhou para: ${input}`);
});
Erro #2: Incompatibilidades de Codificação de Caracteres
O Problema
Codificar uma string em um conjunto de caracteres (por exemplo, ISO-8859-1) e decodificá-la como outro (UTF-8) produz texto ilegível ou o caractere de substituição �.
Sintomas:
Esperado: café
Obtido: café
Esperado: 中文
Obtido: ���
Esperado: Ñoño
Obtido: �o�o
O Que Acontece
//Se o servidor codificou em ISO-8859-1 mas você decodifica como UTF-8:
const encoded = '%C3%A9'; // é em UTF-8
decodeURIComponent(encoded); // → 'é' (correto em UTF-8)
// Mas se foi realmente codificado ISO-8859-1 como %E9:
const wrongEncoding = '%E9';
decodeURIComponent(wrongEncoding); // → 'é' mas exibe errado
A Causa Raiz
- Sistemas legados usando codificações não-UTF-8
- Codificação mista em diferentes partes da aplicação
- Banco de dados configurado com charset errado
- Cabeçalhos HTTP especificando codificação incorreta
A Solução
Correção #1: Padronizar em UTF-8 em todos os lugares
<!-- Em HTML -->
<meta charset="UTF-8">
<!-- Em cabeçalhos HTTP -->
Content-Type: text/html; charset=UTF-8
// Em Express.js
app.use(express.urlencoded({ extended: true, charset: 'utf-8' }));
-- Em MySQL
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Correção #2: Detectar incompatibilidades de codificação
function looksLikeMojibake(str) {
// Padrões comuns de UTF-8 interpretado como ISO-8859-1
const suspiciousPatterns = [
/é|è|à |ç/, // Comum em francês
/£|Â¥|©/, // Moeda e símbolos
/�/, // Caractere de substituição
];
return suspiciousPatterns.some(pattern => pattern.test(str));
}
// Uso
const decoded = decodeURIComponent(encoded);
if (looksLikeMojibake(decoded)) {
console.warn('Possível incompatibilidade de codificação detectada!');
}
Correção #3: Re-codificar se necessário
// Se você sabe que a fonte era Latin-1 mas foi decodificada como UTF-8:
function fixLatin1ToUTF8(str) {
// Esta é uma operação complexa, use uma biblioteca se possível
const encoder = new TextEncoder();
const decoder = new TextDecoder('iso-8859-1');
const bytes = encoder.encode(str);
return decoder.decode(bytes);
}
Prevenção
Impor UTF-8 em todas as camadas:
- Banco de dados: UTF-8 (ou utf8mb4 para MySQL)
- Cabeçalhos HTTP:
Content-Type: charset=UTF-8 - HTML:
<meta charset="UTF-8"> - Arquivos fonte: Salvar como UTF-8
- APIs: Aceitar e retornar UTF-8
Teste Rápido
// Testar com caracteres internacionais
const tests = [
{ text: 'café', lang: 'French' },
{ text: '中文', lang: 'Chinese' },
{ text: 'العربية', lang: 'Arabic' },
{ text: '😀', lang: 'Emoji' },
];
tests.forEach(({ text, lang }) => {
const encoded = encodeURIComponent(text);
const decoded = decodeURIComponent(encoded);
console.assert(decoded === text, `Codificação ${lang} falhou`);
});
Erro #3: Decodificação Incompleta (Problemas Multi-Camada)
O Problema
URLs codificadas várias vezes precisam de múltiplas operações de decodificação. Parar muito cedo deixa sequências de porcentagem na saída.
Exemplo:
Original: Hello World
Codificado 1x: Hello%20World
Codificado 2x: Hello%2520World
Codificado 3x: Hello%252520World
// Se você decodificar apenas uma vez:
decodeURIComponent('Hello%252520World') // → 'Hello%2520World' (ainda codificado!)
O Que Acontece
const doubleEncoded = 'search%253Dhello%2520world';
// Decodificar uma vez
const once = decodeURIComponent(doubleEncoded);
console.log(once); // 'search%3Dhello%20world' - ainda contém %3D e %20!
// Decodificar duas vezes
const twice = decodeURIComponent(once);
console.log(twice); // 'search=hello world' - correto!
A Causa Raiz
- Múltiplos redirecionamentos cada um codificando a URL
- Cadeias de middleware que codificam repetidamente
- Copiar-colar do usuário de URLs já codificadas
- Auto-codificação do framework além da codificação manual
A Solução
Correção #1: Decodificação iterativa até estabilizar
function fullyDecode(str) {
let decoded = str;
let previous = '';
let iterations = 0;
const MAX_ITERATIONS = 5; // Limite de segurança
while (decoded !== previous && iterations < MAX_ITERATIONS) {
previous = decoded;
try {
const temp = decodeURIComponent(decoded);
// Continuar apenas se algo realmente mudou
if (temp !== decoded) {
decoded = temp;
} else {
break;
}
} catch (e) {
// Parar em erro
console.error('Decodificação parou devido a erro:', e);
break;
}
iterations++;
}
console.log(`Decodificado ${iterations} vezes`);
return decoded;
}
// Uso
fullyDecode('Hello%252520World'); // → 'Hello World' (3 iterações)
Correção #2: Contar camadas de codificação
function countLayers(str) {
let count = 0;
let current = str;
while (/%[0-9A-Fa-f]{2}/.test(current) && count < 10) {
try {
const decoded = decodeURIComponent(current);
if (decoded === current) break; // Sem mudança
current = decoded;
count++;
} catch (e) {
break;
}
}
return count;
}
// Uso
console.log(countLayers('Hello%20World')); // 1
console.log(countLayers('Hello%2520World')); // 2
console.log(countLayers('Hello%252520World')); // 3
Correção #3: Detectar e avisar
function decodeWithWarning(str) {
const layers = countLayers(str);
if (layers > 1) {
console.warn(`Codificação multi-camada detectada: ${layers} camadas`);
}
return fullyDecode(str);
}
Prevenção
Evitar codificação dupla:
// ❌ Não faça isso
const alreadyEncoded = encodeURIComponent(userInput);
const doubleEncoded = encodeURIComponent(alreadyEncoded); // Errado!
// ✅ Codificar apenas uma vez
const encoded = encodeURIComponent(userInput);
// ✅ Ou verificar se já está codificado
function encodeOnce(str) {
// Verificação simples: se contém %, assumir que está codificado
if (/%[0-9A-Fa-f]{2}/.test(str)) {
return str; // Já codificado
}
return encodeURIComponent(str);
}
Teste Rápido
const multilayerTests = [
{ input: 'Hello%20World', layers: 1 },
{ input: 'Hello%2520World', layers: 2 },
{ input: '%25252525', layers: 4 }, // %25 codificado 4 vezes
];
multilayerTests.forEach(({ input, layers }) => {
const detected = countLayers(input);
console.assert(detected === layers, `Falhou: esperado ${layers}, obteve ${detected}`);
});
Erro #4: Confusão de Caracteres Reservados
O Problema
Não saber quais caracteres são reservados leva a decisões incorretas de codificação/decodificação.
Erros comuns:
Codificar ? em uma query string // Errado! ? é o delimitador de query
Não codificar & em um valor // Errado! & separa parâmetros
Codificar / em um caminho // Geralmente errado! / é o separador de caminho
O Que Acontece
// Errado: codificar o delimitador de query
const wrongUrl = `/search${encodeURIComponent('?q=test')}`;
// → /search%3Fq%3Dtest (o ? está codificado!)
// Errado: não codificar & em um valor
const name = 'Tom & Jerry';
const badUrl = `/search?query=${name}`;
// → /search?query=Tom & Jerry
// Navegador interpreta como: query=Tom e um parâmetro chamado "Jerry"
// Correto:
const goodUrl = `/search?query=${encodeURIComponent(name)}`;
// → /search?query=Tom%20%26%20Jerry
A Causa Raiz
- Confusão sobre estrutura de URL
- Função de codificação errada (
encodeURIvsencodeURIComponent) - Construção manual de URL sem entender caracteres reservados
Caracteres Reservados em URLs
| Caractere | Significado | Codificar em valores? |
|---|---|---|
: | Separador de protocolo/porta | Sim (em valores) |
/ | Separador de caminho | Não (em caminhos), Sim (em valores) |
? | Início de query string | Não (como delimitador), Sim (em valores) |
# | Identificador de fragmento | Não (como delimitador), Sim (em valores) |
& | Separador de parâmetros | Não (como separador), Sim (em valores) |
= | Separador chave-valor | Não (como separador), Sim (em valores) |
@ | Separador de info do usuário | Sim (geralmente) |
A Solução
Correção #1: Usar a função de codificação correta
// Para codificar URLs COMPLETAS
const fullUrl = 'https://example.com/path with spaces/file.html';
const encoded = encodeURI(fullUrl);
// → 'https://example.com/path%20with%20spaces/file.html'
// Nota: /, :, ? NÃO são codificados
// Para codificar COMPONENTES de URL (valores de query, segmentos de caminho)
const value = 'hello/world?test=value';
const encoded = encodeURIComponent(value);
// → 'hello%2Fworld%3Ftest%3Dvalue'
// Nota: TODOS os caracteres especiais são codificados
Correção #2: Construir URLs adequadamente
// ❌ Maneira errada
const search = 'hello & goodbye';
const url = '/search?q=' + search; // Quebra no &
// ✅ Maneira certa - codificar o valor
const url = '/search?q=' + encodeURIComponent(search);
// ✅ Melhor - usar API de URL
const url = new URL('/search', window.location.origin);
url.searchParams.set('q', search); // Codificação automática
console.log(url.href);
Correção #3: Analisar URLs corretamente
// ❌ Errado - análise manual
const query = window.location.search; // ?name=Tom%20%26%20Jerry
const value = query.split('=')[1]; // 'Tom%20%26%20Jerry'
// Se você esquecer de decodificar, mostrará a versão codificada
// ✅ Correto - usar API de URL
const params = new URLSearchParams(window.location.search);
const value = params.get('name'); // Automaticamente decodificado: 'Tom & Jerry'
Prevenção
Usar utilitários de URL:
// Node.js ou navegadores modernos
const { URL, URLSearchParams } = require('url'); // Node.js
// Ou apenas usar URL e URLSearchParams globais em navegadores
// Construir URLs com segurança
const url = new URL('https://example.com/search');
url.searchParams.append('query', 'hello & goodbye');
url.searchParams.append('page', '1');
console.log(url.toString());
// → https://example.com/search?query=hello+%26+goodbye&page=1
Teste Rápido
const reservedCharTests = [
{ char: '&', desc: 'E comercial' },
{ char: '=', desc: 'Igual' },
{ char: '?', desc: 'Ponto de interrogação' },
{ char: '#', desc: 'Hashtag' },
{ char: '/', desc: 'Barra' },
];
reservedCharTests.forEach(({ char, desc }) => {
const value = `antes${char}depois`;
const encoded = encodeURIComponent(value);
const decoded = decodeURIComponent(encoded);
console.log(`${desc} (${char}):`);
console.log(` Original: ${value}`);
console.log(` Codificado: ${encoded}`);
console.log(` Decodificado: ${decoded}`);
console.assert(decoded === value, `${desc} falhou roundtrip`);
});
Erro #5: Usar Funções/Métodos de Decodificação Errados
O Problema
Diferentes linguagens e frameworks têm diferentes funções de decodificação. Usar a errada produz resultados incorretos.
Erros Comuns
JavaScript:
// ❌ Errado para parâmetros de query
decodeURI('hello%20world%26test');
// → 'hello world%26test' (não decodifica &)
// ✅ Correto
decodeURIComponent('hello%20world%26test');
// → 'hello world&test'
Python:
# ❌ Errado - quote() em vez de unquote()
from urllib.parse import quote
result = quote('hello%20world')
# → 'hello%2520world' (codificado em dobro!)
# ✅ Correto
from urllib.parse import unquote
result = unquote('hello%20world')
# → 'hello world'
PHP:
// Sinais de mais (+) representam espaços em dados de formulário
$encoded = 'hello+world';
// ❌ urldecode() trata + como espaço
$result = urldecode($encoded);
// → 'hello world'
// ✅ Use rawurldecode() para manter + como literal
$result = rawurldecode($encoded);
// → 'hello+world'
// Ou use urldecode() se + deve ser espaço (dados de formulário)
A Solução
Correção #1: Conheça suas funções
JavaScript:
decodeURI()- para URLs inteiras (não decodifica&,=,?, etc.)decodeURIComponent()- para partes de URL (decodifica tudo)
Python:
urllib.parse.unquote()- decodificação padrãourllib.parse.unquote_plus()- decodificar + como espaço (para dados de formulário)
PHP:
urldecode()- decodificar + como espaçorawurldecode()- não decodificar +
Correção #2: Lidar com sinais de mais corretamente
// Se lidando com dados codificados de formulário onde + significa espaço:
function decodeFormData(str) {
return decodeURIComponent(str.replace(/\+/g, ' '));
}
// Uso
decodeFormData('hello+world'); // → 'hello world'
decodeURIComponent('hello+world'); // → 'hello+world' (+ não decodificado)
Correção #3: Testar sua função de decodificação
const testStrings = [
'hello%20world', // Espaço
'hello+world', // Mais
'hello%2Bworld', // Mais codificado
'test%26value', // E comercial
'%E4%B8%AD%E6%96%87', // UTF-8
];
testStrings.forEach(str => {
console.log(`Entrada: ${str}`);
console.log(`decodeURI: ${decodeURI(str)}`);
console.log(`decodeURIComponent: ${decodeURIComponent(str)}`);
console.log('---');
});
Prevenção
Criar funções wrapper:
// Padronizar decodificação em toda a aplicação
function safeDecodeParam(str) {
if (!str) return '';
try {
// Substituir + por espaço para dados de formulário, então decodificar
return decodeURIComponent(str.replace(/\+/g, ' '));
} catch (e) {
console.error('Erro de decodificação:', e);
return str; // Retornar original em erro
}
}
// Usar consistentemente
const userQuery = safeDecodeParam(params.get('q'));
Teste Rápido
// Testar todas as funções de decodificação com a mesma entrada
const testInput = 'hello%20world%26test';
console.log('Testando:', testInput);
console.log('decodeURI: ', decodeURI(testInput));
console.log('decodeURIComponent:', decodeURIComponent(testInput));
console.log('Esperado: hello world&test');
Checklist de Debugging
Quando você encontrar problemas de decodificação de URL, use este checklist:
- Codificação válida? Verificar sequências de porcentagem malformadas (
%ZZ,%2) - Charset correto? Verificar UTF-8 em toda a pilha
- Única ou multi-camada? Contar quantas vezes está codificado
- Caracteres reservados? Garantir tratamento adequado de
&,=,?, etc. - Função correta? Usando
decodeURIComponent()vsdecodeURI()? - Sinais de mais? São para serem espaços ou
+literal? - Tratamento de erro? Envolvido em try-catch?
- Sanitizado? Validado e sanitizado após decodificação?
Ferramentas para Debugging
- Nosso Decodificador de URL: Ferramenta online gratuita com detecção multi-camada
- DevTools do Navegador:
console.log(decodeURIComponent(str)) - Analisador de URL: Visualizar componentes de URL
- Visualizadores Hex: Ver valores de bytes reais
Resumo
| Erro | Correção Rápida | Prevenção |
|---|---|---|
| #1 Formato incorreto | Validar antes de decodificar | Usar funções de codificação adequadas |
| #2 Incompatibilidade de codificação | Padronizar em UTF-8 | UTF-8 em todos os lugares |
| #3 Decodificação incompleta | Decodificar até estabilizar | Evitar codificação dupla |
| #4 Caracteres reservados | Usar encodeURIComponent() | Usar API de URL |
| #5 Função errada | Conheça suas funções | Criar wrappers |
Ao entender e corrigir esses 5 erros comuns, você lidará com decodificação de URL como um profissional. Lembre-se: valide entradas, decodifique com cuidado e sempre teste com casos extremos!
Evite esses erros instantaneamente com nossa ferramenta gratuita de decodificação de URL que lida com todos os casos extremos automaticamente!