Por qué una URL no admite cualquier carácter
Las URL se definieron en el RFC 1738 (1994) para usar un subconjunto de ASCII. Ese conjunto restringido existe porque las direcciones tienen que viajar sin problemas por sistemas pensados para texto ASCII: cabeceras de correo, atributos HTML, archivos de registro y salidas de terminal.
Algunos caracteres tienen un significado reservado dentro de la estructura de una URL: / separa los segmentos de la ruta, ? inicia la cadena de consulta, # marca un fragmento, & separa parámetros y = separa las claves de los valores. Si tus datos contienen esos caracteres, se interpretarían como estructura de la dirección.
La codificación con porcentajes
Cualquier carácter que no puede aparecer tal cual en una URL se representa como %XX, donde XX es el valor hexadecimal de su byte en UTF-8.
- Espacio →
%20(32 en hexadecimal, el carácter de espacio en ASCII) +→%2B/→%2F?→%3F=→%3D&→%26#→%23@→%40"→%22
Los caracteres que no son ASCII (letras acentuadas, emojis, caracteres chinos) se convierten primero a bytes UTF-8 y después se codifica cada byte. El emoji 😀 se convierte en %F0%9F%98%80.
El lío del espacio: %20 frente a +
Hay dos convenciones para codificar los espacios en una dirección web:
%20 es la codificación estándar del RFC 3986 para un espacio literal en cualquier parte de la URL.
+ es una convención de formularios (application/x-www-form-urlencoded) donde el + significa espacio, pero solo en las cadenas de consulta. Viene de los formularios HTML.
De ahí salen errores muy molestos: si codificas un + literal como +, al otro lado puede que lo interpreten como un espacio. Usa siempre %20 para los espacios en el código de tu aplicación y recurre a una biblioteca de codificación en condiciones, en lugar de sustituir los espacios a mano.
Caracteres sin reservar (estos nunca se codifican)
De la A a la Z, de la a a la z, del 0 al 9 y -, _, . y ~: estos pueden aparecer siempre tal cual en una URL.
El contexto importa
**Segmentos de la ruta:** /search/hola%20mundo. La / no debe codificarse, porque separa segmentos, pero los espacios sí.
**Valores de la cadena de consulta:** ?q=hola%20mundo. Los = y & de la estructura no se codifican, pero los valores de los parámetros sí.
**Codificar una URL completa:** cuando metes una dirección dentro de otra (por ejemplo, como parámetro de redirección), hay que codificar la dirección interna entera: ?redirect=https%3A%2F%2Fexample.com%2Fpath
Errores habituales
**Codificar dos veces:** aplicar la codificación a algo que ya estaba codificado. El %20 se convierte en %2520, porque el % pasa a ser %25, y el resultado ya no se decodifica bien.
**Olvidar codificar los valores de un formulario:** un campo que contenga & o = romperá el análisis de la cadena de consulta si no se codifica.
**Codificar la URL entera por error:** hay que codificar los valores de cada parámetro, no toda la dirección. Codificarla entera convierte también /, ? y &, y destroza la estructura.
**Dar por hecho el UTF-8:** algo de código antiguo codifica los caracteres no ASCII como bytes Latin-1 en lugar de UTF-8. Los sistemas modernos esperan UTF-8 en las direcciones (RFC 3986).
Herramientas prácticas
En las herramientas de desarrollo del navegador, la pestaña de red permite hacer clic derecho en una petición y elegir "Copiar como cURL", lo que te da la URL bien codificada. Y usar una clase de URL (new URL() en JavaScript, urllib.parse en Python) siempre es más seguro que construir direcciones concatenando cadenas.
El constructor de parámetros UTM de NoxaKit monta direcciones de seguimiento correctamente codificadas. El analizador de URL sospechosas decodifica las direcciones ofuscadas que usan porcentajes para esconder su destino real.