A codificação de URL, ou codificação percentual (percent-encoding), substitui os caracteres que uma URL não pode transportar literalmente por um % e o valor hexadecimal dos seus bytes, de modo que um espaço vira %20 e um & dentro de um valor deixa de se comportar como separador.
O .NET nos oferece quatro formas de fazer isso: HttpUtility.UrlEncode(), WebUtility.UrlEncode(), Uri.EscapeDataString() e UrlEncoder.Default.Encode(). Elas produzem saídas diferentes para a mesma entrada, e é esse detalhe que faz muita gente perder uma tarde. A ferramenta de codificação e decodificação de URL mostra lado a lado a saída dos quatro métodos (HttpUtility, WebUtility, Uri.EscapeDataString e UrlEncoder) para a mesma entrada, de modo que as diferenças ficam visíveis sem executar este código.
Uri.EscapeDataString() é a opção padrão que vale a pena memorizar. Ele segue a RFC 3986, codifica o espaço como %20 em vez de + e é o equivalente mais próximo que o C# tem do encodeURIComponent() do JavaScript.
O que é a codificação de URI?
A codificação de URI, também chamada de codificação percentual, substitui os caracteres que uma URL não pode transportar literalmente por um sinal de porcentagem seguido do valor hexadecimal dos seus bytes em UTF-8. Um espaço vira %20 e um ponto de interrogação vira %3F.
A regra vem da RFC 3986, que divide os caracteres em dois grupos. Os caracteres não reservados, ou seja, as letras ASCII, os dígitos e -, _, . e ~, trafegam como estão. Os caracteres reservados, como ?, &, =, / e #, têm significado estrutural e marcam onde termina uma parte da URL e começa a seguinte.
Todo o problema está nesse significado estrutural. Um valor que contenha & ou = dividiria a query string no lugar errado, então o codificamos na ida e o decodificamos na volta.
A codificação se aplica a um único valor, e não a um endereço inteiro. Codificar uma URL completa destrói os separadores de que a URL precisa para continuar legível.
Podemos classificar os caracteres de uma URL como reservados ou não reservados. Os caracteres reservados são aqueles que têm um significado especial e, em geral, marcam as várias partes de uma URL. Por exemplo, o caractere ‘?‘ em uma URL indica o início de eventuais parâmetros de query string.
A RFC3986 define quais caracteres são reservados e quais são não reservados.
Caracteres reservados:
! # $ & ‘ ( ) * + , / : ; = ? @ [ ]
Caracteres não reservados:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 – _ . ~
Precisamos codificar os caracteres reservados em uma URL. Para isso, pegamos o valor hexadecimal do byte ASCII do caractere, precedido de um caractere ‘%‘. Por exemplo, o caractere de espaço é codificado como ‘%20‘. Outro nome para a codificação de URL é codificação percentual, por causa do caractere ‘%‘ usado como prefixo.
Um problema relacionado, mas distinto, é o da codificação Base64, que resolve outro problema de transporte: a codificação percentual torna um valor seguro dentro de uma URL, enquanto o Base64 faz bytes arbitrários sobreviverem a um canal que só aceita texto.
Essa codificação é simples de fazer, mas, em vez de escrevermos nós mesmos o código para isso, podemos contar com algumas formas de codificar e decodificar que o .NET nos oferece.
Como codificar e decodificar uma URL com HttpUtility?
HttpUtility fica no namespace System.Web e expõe UrlEncode() e UrlDecode(). Cada um deles recebe uma string e retorna a forma codificada ou decodificada, e cada um tem sobrecargas para um Encoding diferente e para entrada do tipo byte[].
Dois comportamentos da classe pegam muita gente de surpresa. Ela codifica o espaço como + em vez de %20, o que é certo no corpo de um formulário e errado em um segmento de caminho. Ela também escreve os dígitos hexadecimais em minúsculas, então ? volta como %3f, e não como %3F.
A classe continua disponível no .NET atual. Apesar do nome System.Web, ela vem no framework compartilhado, e não com o ASP.NET, então uma aplicação de console simples pode chamá-la sem adicionar nenhum pacote.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
A orientação da própria Microsoft é preferir WebUtility fora de uma aplicação web, que é o tema da próxima seção. Com isso, HttpUtility fica sendo a opção que mantemos em código ASP.NET existente, e não a que escolhemos para código novo.
Nestes exemplos, codificamos uma URL inteira para que as diferenças entre os codificadores fiquem visíveis em uma única string. Em código real, codificamos um único valor, e não o endereço em que ele entra:
using System.Net; using System.Text.Encodings.Web; using System.Web; var url = @"http://example.com/resource?foo=bar with space#fragment"; var httpUtilityEncoded = HttpUtility.UrlEncode(url); Console.WriteLine(httpUtilityEncoded); //http%3a%2f%2fexample.com%2fresource%3ffoo%3dbar+with+space%23fragment var httpUtilityDecoded = HttpUtility.UrlDecode(httpUtilityEncoded); Console.WriteLine(httpUtilityDecoded); //http://example.com/resource?foo=bar with space#fragment
Esses métodos recebem um único parâmetro string com a URL a ser codificada ou decodificada. Por padrão, eles usam a codificação UTF-8, mas, se esse não for o caso, há uma sobrecarga para passar outra codificação. Há também outras sobrecargas para passar um Byte[] em vez de uma string.
Como codificar e decodificar uma URL com WebUtility?
A referência de HttpUtility.UrlEncode no Microsoft Learn resume isso em uma linha: “Para codificar ou decodificar valores fora de uma aplicação web, use a classe WebUtility.” Essa classe fica no namespace System.Net.
O uso é muito parecido com o dos exemplos anteriores, embora não haja sobrecargas:
var webUtilityEncoded = WebUtility.UrlEncode(url); Console.WriteLine(webUtilityEncoded); //http%3A%2F%2Fexample.com%2Fresource%3Ffoo%3Dbar+with+space%23fragment var webUtilityDecoded = WebUtility.UrlDecode(webUtilityEncoded); Console.WriteLine(webUtilityDecoded); //http://example.com/resource?foo=bar with space#fragment
Como usar Uri.EscapeDataString() e UnescapeDataString()?
Uri.EscapeDataString() faz a codificação percentual de um único valor e Uri.UnescapeDataString() a desfaz. Os dois são estáticos, então nunca criamos uma instância de Uri para chamá-los.
A diferença mais visível em relação às duas classes utilitárias é o caractere de espaço. EscapeDataString() escreve %20 e usa os dígitos hexadecimais em maiúsculas, que é o que a RFC 3986 recomenda. Isso faz dele a escolha certa para segmentos de caminho e para qualquer valor que precise coincidir byte a byte com a saída de outro sistema.
Essa assimetria pega muita gente desprevenida. UnescapeDataString() não transforma + de volta em espaço, então um valor codificado por HttpUtility.UrlEncode() e decodificado aqui volta ainda com os sinais de mais. Os testes do projeto de exemplo verificam exatamente isso.
No .NET 9 e posteriores, há sobrecargas com span (EscapeDataString(ReadOnlySpan<char>) e TryEscapeDataString()) para codificar um span sem antes copiá-lo para uma string. UnescapeDataString() ganhou o par correspondente ao mesmo tempo, então um trecho de código crítico para o desempenho pode codificar e decodificar direto em um buffer próprio.
var uriEncoded = Uri.EscapeDataString(url); Console.WriteLine(uriEncoded); //http%3A%2F%2Fexample.com%2Fresource%3Ffoo%3Dbar%20with%20space%23fragment var uriDecoded = Uri.UnescapeDataString(uriEncoded); Console.WriteLine(uriDecoded); //http://example.com/resource?foo=bar with space#fragment
Em código real, codificamos o valor e deixamos o endereço como está, e é assim também que montamos a query string que recebe esses valores codificados:
var searchUrl = $"https://example.com/search?q={Uri.EscapeDataString("bar with space")}";
Console.WriteLine(searchUrl); //https://example.com/search?q=bar%20with%20space
Quando a query string já existe, é esse mesmo codificador que usamos para anexar ou atualizar um valor em uma query string existente sem quebrar os separadores ao redor dele.
Como codificar uma URL com UrlEncoder no ASP.NET Core?
UrlEncoder fica em System.Text.Encodings.Web e é o codificador que o próprio pipeline de saída do ASP.NET Core usa. Para um uso pontual, chamamos UrlEncoder.Default.Encode(). Ele não está no contêiner de injeção de dependência por padrão, então uma Web API precisa recorrer à propriedade estática ou chamar AddWebEncoders() antes.
Ele existe por um motivo diferente do dos outros três. HttpUtility e WebUtility foram escritos para fazer as URLs funcionarem; UrlEncoder foi escrito para tornar a saída segura em um contexto que escolhemos. É por isso que ele é o único codificador cujo comportamento podemos configurar.
Não existe um decodificador correspondente. UrlEncoder foi projetado para funcionar em um único sentido, e a decodificação continua passando por Uri.UnescapeDataString() ou WebUtility.UrlDecode().
A lista de caracteres seguros dele é a mais ampla das quatro, e não a mais restrita. Ele deixa !, (, ), *, @, $, , e ; intactos, enquanto Uri.EscapeDataString() os escapa. Por isso, vale usar EscapeDataString() quando o valor vai para uma URL que nós mesmos estamos montando, e criar um UrlEncoder a partir de um TextEncoderSettings quando precisamos de outra lista de caracteres seguros.
var urlEncoderEncoded = UrlEncoder.Default.Encode(url); Console.WriteLine(urlEncoderEncoded); //http%3A%2F%2Fexample.com%2Fresource%3Ffoo%3Dbar%20with%20space%23fragment
No servidor, a contrapartida do lado da decodificação é recuperar os valores da query string, e lá o framework já os decodificou para nós.
Qual método de codificação de URL usar em C#?
Não existe uma única resposta correta, mas existe uma curta: use Uri.EscapeDataString() para valores que vão em uma URL e WebUtility para a ida e volta de dados de formulário.
As quatro opções diferem em dois pontos visíveis. HttpUtility.UrlEncode() e WebUtility.UrlEncode() codificam o espaço como +, enquanto Uri.EscapeDataString() e UrlEncoder escrevem %20. HttpUtility escreve os dígitos hexadecimais em minúsculas e os outros três, em maiúsculas, então ? vira %3f em um e %3F nos demais.
Elas também diferem em quanto codificam. Em um teste com todos os caracteres ASCII imprimíveis, Uri.EscapeDataString() é o que mais escapa, as duas classes utilitárias ficam no meio e UrlEncoder é o que menos escapa.

E-book gratuito
Sua Web API está pronta para produção?
33 itens para verificar antes de implantá-la, com a correção de cada um. Um PDF gratuito de 76 páginas para .NET 10.
O e-book está em inglês.
Baixe o checklist gratuitoPDF gratuito. Um único e-mail para enviá-lo. Cancele a inscrição quando quiser.
Um aviso antigo deixou de valer. O limite de 65.519 caracteres em Uri.EscapeDataString() é do .NET Framework e do .NET Core, e no .NET 5 e posteriores esse limite não existe. O .NET 10 foi além e removeu o limite geral de comprimento da própria classe Uri, então uma query string longa já não é motivo para escolher um método em vez de outro.
| Método | Namespace | O espaço vira | Dígitos hexadecimais | Decodificar com | Quando usar |
|---|---|---|---|---|---|
HttpUtility.UrlEncode() | System.Web | + | minúsculas | HttpUtility.UrlDecode() | O código ASP.NET existente já o usa |
WebUtility.UrlEncode() | System.Net | + | maiúsculas | WebUtility.UrlDecode() | Ida e volta de valores no formato de formulário |
Uri.EscapeDataString() | System | %20 | maiúsculas | Uri.UnescapeDataString() | Colocar um valor em uma URL |
UrlEncoder.Default.Encode() | System.Text.Encodings.Web | %20 | maiúsculas | não há decodificador; use Uri.UnescapeDataString() | O valor codificado vai parar em código de marcação |
Qual é o equivalente de encodeURIComponent() em C#?
Uri.EscapeDataString() é o equivalente mais próximo em C# do encodeURIComponent() do JavaScript. Os dois fazem a codificação percentual de um único valor, os dois escrevem %20 para o espaço e os dois usam dígitos hexadecimais em maiúsculas.
Eles são parecidos, mas não idênticos. encodeURIComponent() deixa !, ', (, ) e * intactos, e Uri.EscapeDataString() os codifica. Os dois resultados são válidos, porque a codificação desses caracteres é opcional, mas as duas strings não coincidem caractere por caractere se as compararmos.
Na decodificação, os pares são os mesmos. decodeURIComponent() corresponde a Uri.UnescapeDataString(), e nenhum dos dois converte + de volta em espaço.
HttpUtility.UrlEncode() e WebUtility.UrlEncode() não são o equivalente, porque eles de fato codificam o espaço como +. Um valor que chega de um cliente JavaScript deve ser decodificado com Uri.UnescapeDataString() para que os dois lados combinem.
O caminho inverso importa da mesma forma. Um valor que codificamos com HttpUtility.UrlEncode() e entregamos a um navegador precisa de decodeURIComponent() do outro lado, e todo espaço que chegou como + sobrevive à ida e volta como um sinal de mais literal.
Por que new Uri() lança uma UriFormatException?
O construtor Uri espera um URI absoluto, e as duas strings que as pessoas relatam como falhas não se comportam da mesma forma.
new Uri("//example.com") não lança exceção nenhuma. Tanto no Windows quanto no Linux, a chamada funciona e retorna file://example.com/, porque lê a barra dupla inicial como o início de um host, e Uri.TryCreate() também informa sucesso. Acrescente no início o esquema que você realmente quer antes de criar o Uri.
new Uri("/foo") depende da plataforma. No Windows, a chamada lança uma UriFormatException, porque um caminho sem esquema não é um URI absoluto. No Linux e no macOS, a mesma chamada funciona e produz file:///foo, porque lá um caminho sem esquema é um caminho local a partir da raiz.
Para um caminho relativo, passe UriKind.Relative ou UriKind.RelativeOrAbsolute como segundo argumento, o que tem o mesmo comportamento em qualquer plataforma. Uri.TryCreate() informa a falha em vez de lançar exceção, que é o que queremos para uma string que não escrevemos nós mesmos.
Nada disso envolve codificação: EscapeDataString() e UnescapeDataString() são estáticos, então codificar um valor nunca dispara essa exceção.
A RFC 3986 chama o formato //example.com de referência de caminho de rede (network-path reference). Ele é uma forma legítima em código de marcação, e não um erro de digitação; é por isso que chega ao construtor. Quando a string vem de fora do nosso próprio código, o hábito mais seguro é verificar se uma URL é válida antes de usá-la, em vez de deixar o construtor decidir.
Conclusão
Aprendemos como funciona a codificação de URL e descobrimos que o .NET tem várias implementações para codificar e decodificar. Esses métodos diferem ligeiramente na forma como codificam e decodificam as URLs. A implementação que escolhermos vai depender das nossas necessidades específicas.
Testado com .NET 10.0.10.