Localhost est le nom que votre ordinateur utilise pour lui-même. Tapez-le dans un navigateur et la requête n’atteint jamais un réseau : elle sort du navigateur, fait demi-tour à l’intérieur de la machine, et arrive quelques microsecondes plus tard chez ce qui écoute sur ce port.

C’est toute la définition, et elle est vraiment simple. Ce qui ne l’est pas, c’est que le navigateur traite cette adresse différemment de toutes les autres, délibérément, et sur plusieurs points à la fois. Une page servie depuis localhost n’est donc pas la même page servie depuis un domaine, et dans les écarts entre les deux vit un type de bug bien précis : celui qui reste invisible jusqu’au moment du déploiement.

localhost, 127.0.0.1, et le troisième dont personne ne parle

On les emploie indifféremment et ce ne sont pas la même chose.

127.0.0.1
Une adresse. Le loopback IPv4, câblé pour signifier cette machine. Aucune résolution de nom en jeu
::1
La même idée en IPv6, et une adresse différente. Un serveur lié uniquement à IPv4 n'écoute pas ici
localhost
Un nom qui se résout vers l'une des deux. Les deux le plus souvent, et l'ordre est le choix de la machine
0.0.0.0
Pas une adresse que l'on visite. Cela veut dire « écoute sur toutes les interfaces », ce qui rend un serveur joignable depuis votre téléphone sur le même wifi

Cette troisième ligne est à l’origine d’un après-midi précis et exaspérant. localhost est un nom d’hôte, et la machine le résout. Sur un système qui préfère IPv6, localhost devient ::1, et un serveur lié uniquement à 127.0.0.1 n’écoute pas sur ::1. Le résultat est une connexion refusée sur localhost et une page parfaitement fonctionnelle sur 127.0.0.1, ce qui se lit comme si la machine se contredisait. Elle ne se contredit pas. Ce sont deux adresses et votre serveur est sur l’une d’elles.

Pourquoi le navigateur plie ici ses propres règles

C’est la partie qui compte pour les bugs, et la plupart des explications sur localhost l’omettent entièrement.

Les navigateurs exigent un contexte sécurisé pour une longue liste de capacités : service workers, l’API presse-papiers, la géolocalisation, la caméra et le micro, les notifications, et davantage. Contexte sécurisé signifie normalement HTTPS. Mais localhost est considéré comme potentiellement digne de confiance et obtient la dérogation, parce que le trafic ne quitte jamais la machine et qu’il n’y a rien entre les deux pour l’intercepter.

Le raisonnement est solide et la conséquence est un piège. Tout ce qui figure sur cette liste fonctionne sur http://localhost et cesse de fonctionner dès que le même code est servi depuis http://votre-machine-de-staging en HTTP simple. Rien dans votre exécution locale ne vous a dit que la fonctionnalité dépendait d’un contexte sécurisé, parce que la règle vous a été levée en local et est appliquée partout ailleurs.

Les autres différences, qui pointent toutes dans le même sens

Localhost n’est pas une petite version de la production. C’est un environnement différent qui exécute par hasard le même code, et presque chaque différence vous flatte.

Il n’y a pas de réseau. Pas de latence, pas de perte de paquets, pas de wifi capricieux. Toute condition de course qui dépend de l’arrivée d’une requête après une autre se résout localement du côté rapide, et de l’autre côté pour quelqu’un dans un train. Les indicateurs de chargement que personne ne voit jamais sont généralement cela.

Il n’y a ni CDN, ni proxy, ni répartiteur de charge. La compression, la mise en cache, la réécriture d’en-têtes et la mise en tampon des requêtes qui se trouvent devant la production sont toutes absentes. Une réponse qui fonctionne en local peut être transformée par quelque chose au milieu avant qu’un vrai utilisateur ne la voie.

Le système de fichiers est probablement insensible à la casse. Sur macOS et Windows, Logo.svg et logo.svg sont le même fichier. Sur la machine Linux où vous déployez, non, et l’import qui marchait sur l’ordinateur de chaque développeur renvoie une 404 en production.

Les cookies se comportent autrement. localhost fait l’objet d’un traitement particulier par les navigateurs pour les cookies sécurisés, et il n’a pas de domaine enregistrable, si bien que SameSite et le comportement entre sous-domaines que vous n’exercez jamais en local sont exercés immédiatement en production.

Vous êtes un seul utilisateur. Pas de concurrence, pas de pool de connexions sous pression, pas de cache qu’une autre requête a déjà réchauffé.

Chacune de ces choses rend l’exécution locale plus facile que la vraie. C’est le motif qui mérite d’être remarqué : localhost n’échoue pas différemment, il échoue moins, ce qui est précisément ce qui fait de « ça marche en localhost » un signal faible plutôt qu’une garantie.

Ce que la phrase veut vraiment dire

Quand quelqu’un dit qu’une chose fonctionne en local et pas en production, il n’a rien affirmé sur le code. Il a affirmé quelque chose sur deux environnements, et la question utile est de savoir quelle différence en est responsable.

C’est une liste courte, et c’est la même à chaque fois : la dérogation du contexte sécurisé, l’absence de réseau, l’absence des machines intermédiaires, la casse des noms de fichiers, la portée des cookies, et la charge. Un bug qui apparaît au déploiement et pas en local est presque toujours l’un de ces six, et les connaître transforme « ça marche sur ma machine » d’une accusation en une liste de vérification.

C’est aussi pourquoi un environnement de staging existe, et pourquoi lui non plus n’est jamais tout à fait une copie.

Savoir lequel c’était

La difficulté d’un bug d’environnement est que vous ne pouvez pas le voir depuis l’environnement où vous êtes. La personne qui le rencontre est de l’autre côté, avec une page qui ne marche pas et aucun moyen de vous dire pourquoi, et votre machine continue d’insister que tout va bien.

Session Replay

Extension Chrome gratuite. Un clic sur la page qui se comporte mal capture la capture d'écran, la console et le journal réseau, et vous donne un lien à coller dans le ticket.

Obtenir l'extension

La console est l’endroit où un échec de contexte sécurisé s’annonce, et le journal réseau est l’endroit où la réécriture d’un proxy, une 404 sur un fichier qui existe en local, ou un cookie qui n’a jamais été envoyé apparaissent tous. Capturés depuis la machine où cela a réellement cassé, ces deux-là répondent à la question à laquelle votre propre machine ne peut pas répondre.

En un paragraphe

Localhost est le nom que votre ordinateur se donne à lui-même, se résolvant vers 127.0.0.1 ou ::1, et un serveur lié à l’un d’eux n’écoute pas sur l’autre, ce qui explique entièrement que localhost refuse une connexion que 127.0.0.1 accepte. Plus important encore, les navigateurs le traitent comme un contexte sécurisé même en HTTP simple, si bien que les service workers, le presse-papiers, la géolocalisation et la caméra fonctionnent en local et peuvent cesser de fonctionner dès que le même code est servi en HTTP depuis n’importe où ailleurs. Ajoutez le réseau absent, les proxys et le CDN absents, un système de fichiers insensible à la casse, une portée de cookies différente et une charge d’exactement un, et localhost n’est pas une petite production : c’est un environnement qui échoue moins, et c’est pourquoi « ça marche en localhost » ne restreint rien à soi seul.