A codificação de URL — percent-encoding, para usar o nome de verdade — reescreve qualquer caractere que não possa aparecer literalmente em uma URL como um sinal de porcentagem seguido dos dois dígitos hexadecimais do seu byte. Um espaço vira %20, o sinal de número vira %23 e é vira %C3%A9. Ela existe porque uma URL só pode conter um conjunto pequeno de caracteres ASCII, e vários desses caracteres já estão fazendo um trabalho estrutural: o ? que abre uma query string não pode ser ao mesmo tempo um ponto de interrogação dentro do termo de busca de alguém.
Esse é o mecanismo inteiro. Quase tudo o que dá errado com ele vem de um de três enganos: escapar a quantidade errada, escapar duas vezes, ou escapar caracteres quando você deveria estar escapando bytes.
Quais caracteres realmente precisam de codificação?
A RFC 3986, a especificação de 2005 que define a sintaxe das URLs, separa cada caractere em três grupos.
- Não reservados —
A–Z,a–z,0–9e as quatro marcas-._~. Significam a mesma coisa com ou sem escape, então escapá-los é permitido mas inútil. Também faz duas URLs idênticas parecerem diferentes para um cache. - Reservados —
: / ? # [ ] @e! $ & ' ( ) * + , ; =. É a pontuação que dá forma à URL. Se precisa de escape ou não depende inteiramente de onde ela está. - Todo o resto — espaços, aspas, sinais de maior e menor, letras acentuadas, qualquer escrita não latina, qualquer emoji. Sempre escapados, sem precisar julgar nada.
O grupo do meio é onde moram os bugs. Veja ?q=fish&chips. Você queria buscar por "fish&chips"; o servidor lê um parâmetro q com o valor fish, mais um segundo parâmetro chamado chips sem valor. Seus dados foram truncados em silêncio no ampersand e nada deu erro. Codificado direito, o valor é fish%26chips e chega inteiro.
A mesma armadilha pega o # (tudo depois dele é tratado como fragmento e nunca chega ao servidor), a / dentro de um segmento do caminho e o = dentro do valor de um parâmetro. Se você quiser ver no que uma string vira, o codificador de URL daqui mostra os escapes enquanto você digita e, se você colar uma query string inteira, ele separa os parâmetros em uma tabela para você conferir quais valores sobreviveram.
Por que uma única letra acentuada vira dois escapes
O percent-encoding opera sobre bytes, não sobre caracteres. O texto é convertido para UTF-8 primeiro e depois cada byte fora do conjunto seguro é escrito como o seu próprio escape.
é é um caractere, mas dois bytes em UTF-8, então vira %C3%A9. Um emoji comum tem quatro bytes, então vira quatro escapes — doze caracteres na tela para um único glifo. Caracteres chineses e japoneses têm três bytes cada um, e é por isso que uma URL com um termo de busca em chinês fica absurdamente longa.
Isso também é um diagnóstico rápido. Se alguma coisa te entrega %E9 para é, ela está codificando em Latin-1 em vez de UTF-8, e vai estragar qualquer coisa fora do texto da Europa Ocidental. Os navegadores modernos e as bibliotecas padrão usam todos UTF-8; um %E9 solto significa código velho ou uma configuração ruim em algum ponto mais acima.
encodeURI ou encodeURIComponent?
O JavaScript traz duas funções para isso, e elas não são intercambiáveis. A distinção é se você está codificando um valor ou um endereço inteiro.
encodeURIComponent escapa também os caracteres estruturais. Use para o valor de um parâmetro, um segmento do caminho, um pedaço de input do usuário. Rode em um endereço completo e você recebe https%3A%2F%2Fexample.com — tecnicamente correto, completamente inútil como link.
encodeURI deixa a estrutura intacta e só escapa o que nunca pode aparecer cru, como espaços e letras acentuadas. Serve para consertar um endereço que alguém digitou mal. Nunca aponte para input de usuário que você está prestes a inserir em uma URL: ele deixa o & em paz, que é exatamente o caractere que permite a um valor escapar do próprio parâmetro.
A regra que cobre quase todos os casos: se você está colando alguma coisa dentro de uma URL que você mesmo está montando, você quer a versão component.
Um espaço é %20 ou um sinal de mais?
Os dois, dependendo de quem produziu a string, e isso é genuinamente ambíguo em vez de ser um caso de uma convenção estar errada.
O formato application/x-www-form-urlencoded — o que um formulário HTML envia e, portanto, com o que a maioria das query strings se parece — escreve um espaço como +. A RFC 3986, que rege as URLs em geral, escreve como %20 e trata o + como um sinal de mais literal. Uma query string não carrega nenhuma marca te dizendo qual das duas regras a produziu.
A consequência prática são os números de telefone. ?tel=+44123 chega a um monte de servidores como " 44123", com um espaço na frente onde estava o código do país. Se um valor pode conter um mais, codifique como %2B e pare de depender de qualquer uma das duas convenções. %20 é entendido em todo lugar, então prefira ele quando a escolha for sua.
Isso também limita qualquer ferramenta que leia uma query string de volta, incluindo a daqui. A tabela de parâmetros dela decodifica + como espaço, porque esse é o caso mais comum, então um valor em que o mais era literal aparece errado. Nada na string distingue os dois, então nenhuma ferramenta acerta isso todas as vezes.
O que acontece quando algo é codificado duas vezes
Codifique uma string, depois codifique o resultado, e %20 vira %2520 — porque a segunda passada escapa o próprio sinal de porcentagem como %25. O sintoma visível é uma página que mostra Rue%20de%20la%20Paix para o usuário, ou um redirecionamento que cai em um 404 com os escapes ainda no caminho.
Costuma acontecer quando duas camadas fazem cada uma o seu trabalho: seu código codifica um valor e depois um framework ou um helper de redirecionamento codifica a URL inteira de novo na saída. Decodifique uma vez e olhe o que você tem. Se ainda houver escapes, decodifique de novo — escapes sobrando na saída decodificada são a pista.
O que nenhuma ferramenta consegue fazer é te dizer se a codificação dupla foi um erro. Uma URL que legitimamente contém o texto 100% guarda isso como 100%25, e isso é indistinguível de um acidente. Depois que dados codificados duas vezes foram gravados em um banco de dados, a ambiguidade é permanente, e é por isso que o conserto é no ponto da codificação, não em um script de limpeza.
Codificação de URL é criptografia, ou a mesma coisa que Base64?
O percent-encoding não esconde nada. É uma convenção de transporte, reversível por qualquer um com um teclado, e uma query string é um dos lugares menos privados para colocar dados: ela cai nos logs de acesso do servidor, no histórico do navegador e, historicamente, no header Referer enviado para o site seguinte. Tokens, senhas e dados pessoais não têm lugar em uma URL, por mais caprichado que seja o escape.
E também é uma coisa diferente de Base64, embora os dois apareçam juntos com frequência. Base64 transforma binário em texto; o percent-encoding torna o texto seguro para uma URL. O alfabeto padrão do Base64 inclui + e /, que precisam de escape em uma URL, que é exatamente por que a RFC 4648 define uma variante base64url que usa - e _ no lugar.
E não se aplica a nomes de host. Um domínio como müller.de é convertido com punycode para xn--mller-kva.de, um algoritmo completamente separado. Percent-escapes não são válidos em um hostname, ponto.
Onde o percent-encoding ainda causa problemas?
- Barras codificadas no caminho.
%2Fnão é a mesma coisa que/, e muitos servidores e proxies rejeitam ou normalizam isso antes que a sua aplicação veja — o Apache recusa barras codificadas por padrão. Se um segmento do caminho precisa conter uma, teste contra o stack real em vez de confiar no escape. - Fragmentos. Tudo depois do
#fica no navegador. Codificar não muda em nada se o servidor recebe ou não, porque ele nunca recebe. - Tamanho. A especificação não define limite nenhum, mas os servidores limitam a linha da request: o
LimitRequestLinedo Apache vem com 8190 bytes por padrão. O escape infla texto não ASCII em cerca de três vezes, então uma URL longa em japonês bate nesse teto muito antes que uma em inglês. - Não precisar disso de jeito nenhum. Para endereços públicos e legíveis por gente, a melhor jogada costuma ser evitar os caracteres logo de cara. Um slug limpo feito de letras minúsculas, dígitos e hífens é inteiramente não reservado, então sobrevive a ser copiado em um e-mail, em uma janela de chat ou em uma página impressa sem virar uma parede de hex.
O jeito mais rápido de encerrar uma discussão sobre uma URL escapada é colar no codificador e decodificador de URL e olhar: ele sinaliza entrada codificada duas vezes, conta os escapes e quebra uma query string nos seus parâmetros com cada valor decodificado. Roda no seu navegador, o que importa quando a URL que você está depurando tem um token de sessão dentro.
Se a URL vai para dentro de um href e não para a barra do navegador, ela precisa de dois escapes, não um: percent-encoding do valor para a URL e depois escape do resultado para o HTML ao redor. As regras das entidades HTML são a metade que este post não cobre, e fazer isso na ordem errada quebra o link com a mesma eficiência que pular um.
Perguntas frequentes
O que é codificação de URL?
Codificação de URL, ou percent-encoding, substitui os caracteres que não podem aparecer literalmente em uma URL por um sinal de porcentagem e os dois dígitos hexadecimais do byte deles. Um espaço vira %20 e um ampersand vira %26. Existe para que os dados possam viajar dentro de uma URL sem serem confundidos com a pontuação que dá estrutura à URL.
O que significa %20 em uma URL?
É um espaço. O número hexadecimal 20 é o valor do byte do caractere de espaço em ASCII, e um espaço cru não é permitido em uma URL. Você também vai ver espaços escritos como um sinal de mais, que é a convenção usada pelos envios de formulário HTML.
Quais caracteres precisam ser codificados em uma URL?
Letras, dígitos e as quatro marcas - . _ ~ nunca precisam de codificação. Tudo fora do ASCII sempre precisa. A pontuação do meio — / ? # & = + e o resto — precisa de codificação quando está dentro de um valor em vez de funcionar como estrutura.
Codificação de URL é a mesma coisa que criptografia?
Não. O percent-encoding é uma convenção pública e reversível, sem chave e sem segredo; qualquer um decodifica na hora. Query strings também acabam nos logs do servidor e no histórico do navegador, então valores sensíveis não deveriam entrar em uma URL de jeito nenhum.
Como eu decodifico uma URL cheia de sinais de porcentagem?
Cole em um decodificador e leia o resultado. Se a saída decodificada ainda tiver percent-escapes, ela foi codificada duas vezes e precisa de uma segunda passada. Se o decodificador reportar UTF-8 inválido, o que produziu a URL usou outro conjunto de caracteres, normalmente Latin-1.
Última atualização 19 de setembro de 2026