8 min de leituraEngenharia

Codificação de URL Explicada: Quais Caracteres Escapar

A codificação de URL substitui um caractere por um sinal de porcentagem e dois dígitos hexadecimais para que ele não seja lido como sintaxe de URL. Quais caracteres precisam disso, e onde isso quebra.

Marius Voß
DevRel · edge infra
Codificação de URL mostrada como uma query string onde um espaço e um ampersand se tornam sequências de porcentagem dentro de um valor de parâmetro

A codificação de URL substitui um caractere por um sinal de porcentagem e dois dígitos hexadecimais: um espaço se torna %20, um ampersand se torna %26, um ponto de interrogação se torna %3F. O objetivo é impedir que um caractere seja lido como sintaxe de URL quando você quis dizer que ele era dado. Nada mais.

O motivo de parecer mais difícil do que isso é que quase toda pergunta sobre o assunto é, na verdade, uma pergunta sobre escopo. Quais caracteres, em qual parte da URL, escapados por qual camada? Erre o escopo e você terá uma de duas falhas clássicas: um parâmetro de rastreamento que é truncado silenciosamente, ou um destino que chega como https%3A%2F%2Fexample.com e retorna 404. Este post cobre os dois conjuntos de caracteres que decidem a resposta, os lugares onde as regras mudam, e como verificar o que um link está realmente carregando. Para o panorama mais amplo do que um redirecionamento faz com tudo isso, veja tipos de redirecionamento.

Os Dois Conjuntos Que Decidem Tudo

A RFC 3986, seção 2.3 define um conjunto não reservado que nunca precisa de codificação: letras, dígitos, e exatamente quatro sinais de pontuação - hífen, ponto, sublinhado, til. Se o seu valor contém apenas esses, não há nada a fazer.

Tudo o mais cai em uma de duas categorias. Caracteres reservados carregam significado estrutural: : / ? # [ ] @ separam as partes de uma URL, e ! $ & ' ( ) * + , ; = separam coisas dentro dessas partes. A seção 2.2 os lista. Eles são legais como sintaxe e precisam ser codificados quando aparecem como dado. O resto é tudo o que está fora do ASCII, que é codificado byte a byte depois de ser convertido para UTF-8 - motivo pelo qual uma letra acentuada geralmente custa seis caracteres em vez de três.

Isso dá a única regra que vale memorizar: codifique um caractere quando ele for dado e, de outra forma, seria lido como sintaxe. Um ampersand entre dois parâmetros é sintaxe. Um ampersand dentro de um nome de campanha é dado, e se você deixá-lo como está, a lista de parâmetros termina ali.

Uma query string onde o valor da campanha contém um espaço e um ampersand, mostrada codificada corretamente dentro do valor e incorretamente em toda a URL

Codifique o Valor, Não a URL

Este é o erro que vejo com mais frequência, e é sempre da mesma forma. Alguém tem uma URL, sabe que ela precisa de codificação, então cola a coisa toda em um codificador e obtém:

https%3A%2F%2Fexample.com%2Fspring%3Futm_campaign%3Dspring%20sale

Essa string não é uma URL. É um pedaço de texto com formato de URL que só pode ser um valor dentro de outra URL - que é exatamente onde ela pertence quando você está passando um destino por um redirecionador, e exatamente onde ela não pertence quando você está tentando abri-la.

O tratamento correto codifica cada valor por si só:

https://example.com/spring?utm_campaign=spring%20sale&utm_source=flyer

Esquema, host, separadores de caminho e os ? e & são deixados como sintaxe. Só o valor mudou. Toda linguagem traz duas funções para essa distinção, e escolher a errada é a outra metade do problema: a página da MDN sobre encodeURIComponent é direta ao dizer que encodeURI deliberadamente deixa os caracteres reservados intactos porque espera uma URI inteira, enquanto encodeURIComponent os escapa porque espera um fragmento de uma. Valores querem encodeURIComponent. Em Python isso é urllib.parse.quote, em Go url.QueryEscape, em PHP rawurlencode.

Espaço É %20, Exceto Quando É um Sinal de Mais

Ambos estão corretos, em lugares diferentes, e essa é a coisa mais confusa de todo o assunto.

Em um caminho ou em uma URI genérica, um espaço é %20. Em uma query string construída da forma como um formulário HTML constrói uma, um espaço é +, porque é isso que a serialização application/x-www-form-urlencoded no padrão de URL da WHATWG especifica. Ambas as formas são lidas como um espaço por qualquer parser de query do lado do servidor que você provavelmente vai encontrar.

A armadilha é a direção contrária. Se um sinal de mais é dado - um número de telefone, um termo de busca, uma campanha chamada spring+summer - ele precisa ser escrito %2B. Deixado sem codificação em uma query string, ele se torna um espaço, e você vai passar uma tarde se perguntando por que o número no seu CRM perdeu o código do país.

CaractereCodificadoPor que isso importa
espaço%20 ou ++ só dentro de uma query string, %20 em todo o resto
&%26Sem codificação, a lista de parâmetros termina ali
?%3FSem codificação, tudo depois dele se torna a query
#%23Sem codificação, o resto nunca chega ao servidor
+%2BSem codificação em uma query, ele chega como um espaço
%%25Sem codificação, os próximos dois caracteres são engolidos

A linha do # merece uma nota, porque é a que produz o relato de bug mais confuso. Um fragmento nunca é enviado ao servidor. Coloque um # sem codificação em um destino de redirecionamento e o servidor vê uma URL truncada, enquanto a barra de endereço do navegador ainda parece correta, então a pessoa que relata o problema jura que o link está bom.

Se você constrói URLs de campanha manualmente com mais do que uma frequência ocasional, pare: nosso construtor de UTM codifica cada valor enquanto você digita, e convenções de nomenclatura de UTM cobre como escolher valores que não precisam de codificação alguma. Encurte o resultado no seu próprio domínio e a confusão de caracteres codificados deixa de ser algo que alguém precisa olhar.

Dupla Codificação, e Como Identificá-la

Dupla codificação é o que acontece quando um valor passa por duas camadas que cada uma faz o seu trabalho. O próprio sinal de porcentagem é um caractere que precisa ser escapado, então %20 se torna %2520, e %2520 se torna %252520.

Os sintomas são reconhecíveis depois que você já os viu. Um título de página que exibe spring%20sale para um visitante real. Um parâmetro que chega na analytics com sequências de escape visíveis. Um redirecionamento que funciona no primeiro salto e falha no segundo. A causa quase sempre é uma chamada de codificação envolvendo um valor que já chegou codificado, muitas vezes porque ele saiu de um banco de dados que armazenava a forma codificada.

A correção é decidir qual camada é dona da codificação e deixar as outras sem tocar nisso. Decodifique uma vez quando ler um valor, codifique uma vez quando escrevê-lo em uma URL, e nunca faça as duas coisas na mesma função.

Um valor passando por duas camadas de codificação, de modo que um espaço se torna %20 e depois %2520, com o sintoma visível no navegador

Onde Isso Morde na Prática

Três lugares, na ordem em que você provavelmente vai encontrá-los.

Parâmetros de rastreamento. Um valor de campanha com um ampersand sem codificação trunca a lista de parâmetros, então a sessão chega na sua analytics como tráfego direto e a campanha não recebe nenhum crédito. Nada dá erro. Parâmetros UTM não aparecem no GA4 cobre o diagnóstico do lado dos relatórios, e navegadores removem parâmetros UTM cobre a outra razão pela qual um parâmetro pode desaparecer entre o clique e a página.

Redirecionamentos. As regras do servidor recodificam de forma inconsistente, e se a query string sobrevive ou não depende da diretiva que você usou. Um redirecionamento 301 no .htaccess tem a tabela completa para o Apache; a versão resumida é que uma regra que substitui a query string vai descartar a sua silenciosamente.

Códigos QR. A codificação aumenta o tamanho do payload, e o tamanho do payload decide o quão denso é o código impresso. Cada espaço custa três caracteres em vez de um, cada letra acentuada, seis. Uma URL de rastreamento com alguns nomes de campanha codificados pode empurrar um código uma ou duas versões acima, o que faz uma diferença real no tamanho de um cartão de visita - QR Code não lê coloca o tamanho do payload entre as quatro causas exatamente por esse motivo. Codificar um link curto em vez da URL completa é a correção mais barata disponível.

Dois comandos resolvem quase toda discussão. O primeiro mostra o que o servidor recebe depois de um redirecionamento:

curl -sI 'https://example.com/spring?utm_campaign=spring%20sale' | grep -i '^location'

O segundo constrói a codificação para você em vez de confiar nos seus dedos, o que é útil quando um valor contém vários infratores ao mesmo tempo:

curl -G --data-urlencode 'utm_campaign=spring & summer sale' \
  --data-urlencode 'utm_source=flyer' \
  -o /dev/null -w '%{url_effective}\n' https://example.com/spring

Leia a saída como dado, não como decoração. Se você vir %2520, você tem um problema de dupla codificação; se você vir um valor terminando antes da hora, você tem um separador sem codificação; e se você vir %3A%2F%2F no início, você codificou a URL inteira. Nosso verificador de links faz a parte do redirecionamento em um navegador, se você preferir não abrir um terminal.

O hábito que vale a pena construir é olhar para a URL final uma vez, a olho nu, antes de a campanha ser publicada. Bugs de codificação são invisíveis em um navegador e óbvios em um terminal, e eles custam a sua atribuição, não o seu uptime, motivo pelo qual sobrevivem tanto tempo.

Leia a Série Principal

Este post está no cluster de engenharia. Para o lado do redirecionamento, tipos de redirecionamento cobre todo código de status e método do lado do cliente, e como funcionam os encurtadores de URL cobre o que acontece entre o clique e a página.

Relacionados no blog

Perguntas frequentes

O que é codificação de URL?

Substituir um caractere por um sinal de porcentagem seguido do seu valor em bytes em hexadecimal, para que o caractere não possa ser confundido com sintaxe de URL. Um espaço se torna %20, um ampersand se torna %26, um ponto de interrogação se torna %3F. O mecanismo é definido na RFC 3986 e também é chamado de percent-encoding.

Quais caracteres precisam ser codificados em URL?

Qualquer coisa fora do conjunto não reservado, que a RFC 3986 define como letras, dígitos, e os quatro caracteres hífen, ponto, sublinhado e til. Tudo o mais é pontuação reservada que carrega significado estrutural, ou um byte fora do ASCII, e ambos precisam ser codificados com percent-encoding quando aparecem dentro de um valor em vez de como sintaxe.

Devo codificar a URL inteira ou apenas partes dela?

Apenas as partes. Passar uma URL completa por um codificador transforma https://example.com em https%3A%2F%2Fexample.com, que não é mais uma URL de forma alguma. Codifique cada valor de parâmetro de query separadamente, e cada segmento de caminho separadamente, e deixe o esquema, o host e os separadores em paz.

Um espaço é %20 ou um sinal de mais?

Ambos, em lugares diferentes. Em um caminho e em uma URI genérica, um espaço é %20. Em uma query string construída da forma como os formulários HTML constroem uma, um espaço é um sinal de mais, porque é isso que a serialização application/x-www-form-urlencoded especifica. Um sinal de mais literal dentro de um valor de query, portanto, precisa ser escrito %2B, ou será lido como um espaço.

O que é dupla codificação?

Codificar algo que já estava codificado, de modo que %20 se torna %2520 porque o próprio sinal de porcentagem é escapado para %25. O sintoma é uma página que exibe um %20 literal no seu texto, ou um parâmetro que chega com sequências de escape visíveis. Quase sempre é um valor que passou por duas camadas, cada uma delas codificando-o de boa vontade.

Por que caracteres codificados tornam um código QR mais difícil de ler?

Porque cada um custa três caracteres em vez de um. Um espaço é um caractere de intenção e três de payload, então alguns deles já podem empurrar o código uma ou duas versões acima, o que significa mais módulos na mesma área impressa. Codificar uma URL de rastreamento longa em um QR é uma das formas mais rápidas de criar um código que só é lido a curta distância.

Experimente Elido

Cole uma URL, obtenha um link curto

Sem cadastro. O link vive 30 dias. Cadastre-se para mantê-lo para sempre.

Grátis, sem necessidade de registo · 2 por dia

Experimente o Elido

Encurtador de URL hospedado na UE: domínios personalizados, análises profundas e API aberta. Plano gratuito - sem cartão de crédito.

Tags
url encoding
percent encoding
encodeuricomponent
query string
utm parameters
url shortener

Continuar lendo