Localhost es el nombre que su ordenador usa para sí mismo. Escríbalo en un navegador y la petición no llega nunca a una red: sale del navegador, da la vuelta dentro de la máquina, y llega a lo que esté escuchando en ese puerto unos microsegundos después.

Esa es toda la definición, y es genuinamente simple. Lo que no es simple es que el navegador trata esta dirección de forma distinta a cualquier otra a propósito, en varios aspectos a la vez. Así que una página servida desde localhost no es la misma página servida desde un dominio, y en los huecos entre ambas vive un tipo concreto de bug: el que es invisible hasta el momento en que despliega.

localhost, 127.0.0.1, y el tercero que nadie menciona

Se usan indistintamente y no son lo mismo.

127.0.0.1
Una dirección. El loopback de IPv4, cableado para significar esta máquina. Sin resolución de nombres de por medio
::1
La misma idea en IPv6, y una dirección distinta. Un servidor atado solo a IPv4 no escucha aquí
localhost
Un nombre que resuelve a una de las anteriores. Normalmente a las dos, y el orden lo decide la máquina
0.0.0.0
No es una dirección que se visite. Significa "escucha en todas las interfaces", que es lo que hace a un servidor alcanzable desde su móvil en el mismo wifi

Esa tercera fila es el origen de una tarde concreta y exasperante. localhost es un nombre de host, y la máquina lo resuelve. En un sistema que prefiere IPv6, localhost se convierte en ::1, y un servidor atado solo a 127.0.0.1 no escucha en ::1. El resultado es una conexión rechazada en localhost y una página funcionando perfectamente en 127.0.0.1, lo que se lee como si la máquina se contradijera a sí misma. No lo hace. Son dos direcciones y su servidor está en una.

Por qué el navegador dobla aquí sus propias reglas

Esta es la parte que importa para los bugs, y la mayoría de las explicaciones sobre localhost la dejan fuera por completo.

Los navegadores exigen un contexto seguro para una lista larga de capacidades: service workers, la API del portapapeles, geolocalización, cámara y micrófono, notificaciones y más. Contexto seguro normalmente significa HTTPS. Pero localhost se considera potencialmente fiable y recibe la exención, porque el tráfico nunca sale de la máquina y no hay nada en medio que pueda interceptarlo.

El razonamiento es sólido y la consecuencia es una trampa. Todo lo de esa lista funciona en http://localhost y deja de funcionar en el momento en que el mismo código se sirve desde http://su-maquina-de-staging por HTTP plano. Nada en su ejecución local le dijo que la función dependía de un contexto seguro, porque la regla se le perdonó localmente y se aplica en todas partes.

Las otras diferencias, que apuntan todas en la misma dirección

Localhost no es una versión pequeña de producción. Es un entorno distinto que resulta que ejecuta el mismo código, y casi todas las diferencias le halagan.

No hay red. Ni latencia, ni pérdida de paquetes, ni wifi inestable. Toda condición de carrera que dependa de que una petición llegue después de otra se resuelve localmente por el camino rápido y para alguien en un tren por el otro. Los spinners de carga que nadie ve nunca suelen ser esto.

No hay CDN, ni proxy, ni balanceador. La compresión, el cacheo, la reescritura de cabeceras y el almacenamiento temporal de peticiones que están delante de producción faltan todos. Una respuesta que funciona localmente puede ser transformada por algo intermedio antes de que un usuario real la vea.

El sistema de archivos probablemente no distingue mayúsculas. En macOS y Windows, Logo.svg y logo.svg son el mismo archivo. En la máquina Linux a la que despliega, no, y el import que funcionaba en el ordenador de todos los desarrolladores da 404 en producción.

Las cookies se comportan de otra manera. Los navegadores tratan localhost como caso especial para las cookies seguras, y no tiene dominio registrable, así que SameSite y el comportamiento entre subdominios que nunca ejercita localmente se ejercita de inmediato en producción.

Usted es un solo usuario. Sin concurrencia, sin un pool de conexiones bajo presión, sin una caché que otra petición ya haya calentado.

Cada una de esas hace la ejecución local más fácil que la real. Ese es el patrón que merece la pena notar: localhost no falla de otra manera, falla menos, que es exactamente lo que convierte “funciona en localhost” en una señal débil en lugar de en algo tranquilizador.

Qué significa realmente la frase

Cuando alguien dice que algo funciona en local y no en producción, no ha hecho una afirmación sobre el código. Ha hecho una afirmación sobre dos entornos, y la pregunta útil es cuál diferencia es la responsable.

Esa es una lista corta, y es la misma cada vez: la exención del contexto seguro, la ausencia de red, la ausencia de las cajas intermedias, las mayúsculas de los nombres de archivo, el alcance de las cookies, y la carga. Un bug que aparece al desplegar y no en local es casi siempre uno de esos seis, y conocerlos convierte “funciona en mi máquina” de una acusación en una lista de comprobación.

También es por lo que existe un entorno de staging, y por lo que él tampoco es nunca del todo una copia.

Averiguar cuál fue

La dificultad de un bug de entorno es que no puede verlo desde el entorno en el que está. La persona que lo sufre está al otro lado, con una página que no funciona y sin manera de decirle por qué, y su máquina sigue insistiendo en que todo va bien.

Session Replay

Extensión gratuita de Chrome. Un clic en la página que se comporta mal captura la captura de pantalla, la consola y el registro de red, y le da un enlace para pegar en el ticket.

Obtener la extensión

La consola es donde un fallo de contexto seguro se anuncia, y el registro de red es donde aparecen la reescritura de un proxy, un 404 sobre un archivo que existe en local, o una cookie que nunca se envió. Capturados desde la máquina donde se rompió de verdad, esos dos responden la pregunta que su propia máquina no puede.

En un párrafo

Localhost es el nombre que su ordenador usa para sí mismo, resolviendo a 127.0.0.1 o ::1, y un servidor atado a uno de ellos no escucha en el otro, que es toda la explicación de que localhost rechace una conexión que 127.0.0.1 acepta. Más importante aún, los navegadores lo tratan como contexto seguro incluso por HTTP plano, así que los service workers, el portapapeles, la geolocalización y la cámara funcionan en local y pueden dejar de funcionar en cuanto el mismo código se sirve por HTTP desde cualquier otro sitio. Sume la red ausente, los proxies y la CDN ausentes, un sistema de archivos que no distingue mayúsculas, un alcance de cookies distinto y una carga de exactamente uno, y localhost no es una producción pequeña: es un entorno que falla menos, que es por lo que “funciona en localhost” no acota nada por sí solo.