Por qué las URLs necesitan codificación
Las URLs tienen un alfabeto pequeño. Letras, dígitos, unos pocos signos de puntuación, y ya. Cualquier otra cosa (espacios, acentos, caracteres especiales) se tiene que reescribir en una forma que la URL sepa transportar.
La forma estándar es “percent encoding”: cada carácter prohibido se vuelve un signo de porcentaje seguido de dos dígitos hex. Un espacio se vuelve %20, una é acentuada se vuelve %C3%A9, y así.
Esto suena simple. En la práctica, la mitad de los bugs de URL en producción vienen de codificar las cosas equivocadas, codificar las correctas dos veces, o usar la función equivocada para el trabajo.
Las dos funciones de JavaScript, y cuándo gana cada una
JavaScript trae dos funciones de codificación URL. Tienen pinta parecida y son muy distintas.
encodeURIComponent() es la que casi siempre quieres. Codifica todo excepto letras, dígitos y un pequeño conjunto de caracteres seguros. Trata su input como una sola pieza (un valor de parámetro, un campo de query, un segmento de ruta) y hace esa pieza segura sin importar dónde caiga.
encodeURI() asume que su input ya es una URL completa y solo codifica los espacios y acentos que encuentra. Deja deliberadamente :, /, ?, &, # en paz porque son parte de la estructura de la URL.
La regla:
- Codificar un valor que va a un parámetro o segmento de ruta:
encodeURIComponent. - Codificar una cadena que se supone que es una URL completa con estructura ya colocada:
encodeURI.
Usar encodeURI donde necesitabas encodeURIComponent es el bug que convierte ?q=gatos&perros en ?q=gatos&perros en lugar de ?q=gatos%26perros. El & literal del usuario dentro de su búsqueda se vuelve un separador de parámetros real, y la URL significa algo distinto a lo que querías.
La ambigüedad del signo plus
Las URLs tienen dos formas de codificar un espacio: %20 y +.
%20 es correcto en todas partes. + solo es válido en la parte de query string de una URL (la parte después del ?), y es un legado de la codificación de formularios HTML.
La mayoría de navegadores manejan ambos. Algunos servidores no. Si codificas un segmento de ruta usando + para los espacios, algunos servidores lo entregan como un + literal, y tu archivo con nombre Mi Archivo.pdf se vuelve inaccesible porque la URL va a Mi+Archivo.pdf.
Regla segura: usa siempre %20 para los espacios. encodeURIComponent lo hace correcto, los codificadores de formulario que vienen con frameworks normalmente no.
El codificador URL de AldeaCode te deja convertir en ambas direcciones y enseña la diferencia entre codificación de ruta y codificación de query. Útil cuando recibes una URL malformada y tienes que averiguar qué codificación se rompió.
Los caracteres reservados
El spec de URL reserva ciertos caracteres porque tienen significado en la estructura:
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
Estos caracteres no siempre se codifican. Algunos solo necesitan codificación en ciertas posiciones. : está bien en la ruta pero rompe en el nombre de un parámetro de query. & está bien fuera del query string pero separa parámetros dentro.
encodeURIComponent es la elección segura porque codifica todos. Es lo más cercano a “codifica esto, da igual a dónde vaya”.
Dos caracteres confunden a la gente a menudo:
~(tilde): no reservado, no necesita codificación, a menudo se codifica igualmente por implementaciones paranoicas.'(apóstrofe): no reservado, pero algunas implementaciones viejas lo codifican. Si tu URL tiene%27por todas partes, es un apóstrofe que no necesitaba codificarse.
Doble codificación: el bug silencioso
El bug de URL más común es codificar algo dos veces. Una al salir, otra por un framework o middleware que no se dio cuenta de que ya estaba codificado.
Un espacio se vuelve %20. Codifica el valor codificado otra vez, y %20 se vuelve %2520 (el porcentaje en sí se codifica como %25). La URL todavía funciona en el sentido de que parsea, pero el valor que recibe el servidor es %20, no un espacio.
Si tus URLs acaban con %25 por todas partes, estás doble codificando en algún sitio. El arreglo es encontrar el segundo codificador y quitarlo, o decodificar una vez antes de codificar.
Codificar acentos y caracteres no latinos
UTF-8 es el estándar para caracteres no ASCII en URLs. El carácter é se vuelve %C3%A9 (dos bytes de UTF-8, codificados como cuatro caracteres en total con percent encoding).
Algunos sistemas viejos codifican caracteres acentuados en su forma Latin-1 (%E9 para é) en lugar. La mayoría de parsers modernos manejan ambos, algunos no. Si dudas, usa codificación UTF-8, que es lo que produce encodeURIComponent.
Para caracteres asiáticos, emojis y otros caracteres UTF-8 multi byte, la regla es la misma. encodeURIComponent los maneja correctamente. Cada carácter se expande a varios bytes con percent encoding, así que una URL con unos pocos emojis puede crecer notablemente.
Una regla práctica de 30 segundos
Para cualquier valor que va a una URL:
const seguro = encodeURIComponent(valor);
const url = `https://example.com/api?q=${seguro}`;
Para una URL que montas desde cero, codifica cada pieza por separado. No te fíes de una cadena de un usuario como ya segura para URLs. No ejecutes nunca encodeURI sobre un valor, solo sobre una URL completa.
El codificador URL, el limpiador URL cuando necesitas quitar parámetros de tracking, y el buscar y reemplazar para normalizar URLs en lote, todos corren en tu navegador, sin subida. Dos reglas acaban con la mayoría de bugs de codificación: usa encodeURIComponent para los valores, no codifiques nunca la misma cadena dos veces.