12k
All articles

Portnummern in der Entwicklung durch benannte URLs ersetzen

Ersetzen Sie localhost-Ports durch benannte URLs mit .localhost, Reverse Proxy oder portless, um Portkonflikte, Cookie-Leaks und falsche Tabs zu vermeiden.

OpenReplay Team
OpenReplay Team
Portnummern in der Entwicklung durch benannte URLs ersetzen

Ein localhost-Domainname ist ein menschenlesbarer Hostname wie app.localhost, der auf 127.0.0.1 auflöst und es jedem lokalen Service ermöglicht, eine stabile Adresse anstelle einer wechselnden Portnummer zu behalten.

Sie kennen diesen Moment wahrscheinlich: Drei Dev-Server laufen, Sie wechseln zurück zum Tab mit localhost:3000, um einen Fix zu prüfen – und die App, die Sie dort anblickt, ist das Projekt von gestern. localhost:3000 durch app.localhost zu ersetzen, beseitigt gleich ein ganzes Bündel alltäglicher Ärgernisse (Portkonflikte, driftende URLs, Cookie-Vermischung und das Problem des „falschen Tabs”), denn jede App erhält ihren eigenen Hostnamen und damit ihren eigenen, isolierten Browser-Scope. Dieser Artikel behandelt drei Wege dorthin: die eingebaute .localhost-TLD, einen selbst gebauten Reverse Proxy und portless, einen speziell dafür entwickelten lokalen Proxy von Vercel Labs.

Die wichtigsten Erkenntnisse

  • Die .localhost-TLD ist durch RFC 6761 für die Loopback-Nutzung reserviert, sodass jeder Name darunter in Chrome, Firefox und Edge ohne Eintrag in der hosts-Datei auf 127.0.0.1 auflöst. Safari, das dem System-Resolver den Vortritt lässt, benötigt möglicherweise trotzdem einen Eintrag.
  • Da Browser Cookies nach Host abgrenzen und den Port ignorieren, bleiben app.localhost und api.localhost getrennt, während localhost:3000 und localhost:3001 sich denselben Cookie-Speicher teilen.
  • Die .localhost-TLD allein entfernt den Port nicht; Ihre App lauscht weiterhin auf einem Port, Sie benötigen also einen Reverse Proxy, der den Hostnamen auf diesen Port abbildet.
  • portless (Vercel Labs, noch vor Version 1.0) weist jeder App über die Umgebungsvariable PORT einen ephemeren Port im Bereich 4000–4999 zu und leitet eine stabile name.localhost-URL dorthin – mit HTTPS und HTTP/2 standardmäßig aktiviert.
  • Eine stabile, benannte URL, die in einer Agents-Datei festgehalten ist, lässt KI-Coding-Tools den richtigen Service ansprechen, statt zwischen Port 3001 und 8080 zu raten.

Warum sind benannte URLs besser als Portnummern?

Portbasierte lokale Entwicklung geht auf vorhersehbare Weise kaputt, sobald Sie mehr als einen Service betreiben. Starten Sie eine zweite App auf einem belegten Port, wirft Node EADDRINUSE. Frameworks, die automatisch hochzählen, umgehen den Absturz, führen aber Drift ein: Ihr Blog läuft heute auf localhost:3001 und morgen auf localhost:3002, Lesezeichen verfallen also, und der Browser-Verlauf für localhost:3000 wird zu einem unnavigierbaren Haufen zusammenhangloser Projekte. Beenden Sie einen Server und starten einen anderen auf dem freigewordenen Port, liefert ein offen gelassener Tab stillschweigend das andere Projekt aus: das Problem des „falschen Tabs”.

Der subtilere Fehlerfall ist das Durchsickern von Zustand. Browser grenzen Cookies nach Host ab und ignorieren den Port, sodass localhost:3000 und localhost:3001 in denselben Cookie-Speicher schreiben. Session-State einer App leckt in eine andere. Benannte Subdomains beheben das auf Origin-Ebene: app.localhost und api.localhost sind verschiedene Hostnamen, trennen Cookies also sauber – und da die Same-Origin-Policy auf Schema, Host und Port basiert, trennen sie auch localStorage und sessionStorage. Microsofts Leitfaden zur TLD macht denselben Punkt: Jeder lokalen App einen eigenen Namen zu geben, hält namensgebundene Ressourcen wie Cookies auseinander, und der Name in der Adressleiste zeigt Ihnen auf einen Blick, welche App Sie gerade vor sich haben.

Was ist die .localhost-TLD?

Der einfachste Mechanismus für benannte URLs ist bereits in Ihrem Browser enthalten. RFC 6761 reserviert die .localhost-TLD – und jeden Namen darunter – für die Loopback-Adresse; deshalb antwortet app.localhost ganz ohne Konfiguration auf 127.0.0.1. Chrome, Firefox und Edge erledigen diese Auflösung intern und bilden jeden *.localhost-Namen auf 127.0.0.1 oder ::1 ab, sodass ein solcher Name als Alias für alles fungiert, was schon auf localhost ausgeliefert wird. Auf Safari gilt es zu achten: Es übergibt den Namen stattdessen an den System-DNS-Resolver, und nicht jede Resolver-Konfiguration antwortet für .localhost-Subdomains – dort brauchen Sie möglicherweise einen /etc/hosts-Eintrag.

Es gibt einen Haken: Die TLD allein entfernt den Port nicht. Ihre App lauscht weiterhin auf :3000, und app.localhost ohne Port landet einfach auf app.localhost:80, wo nichts lauscht. Um die Nummer tatsächlich loszuwerden, brauchen Sie einen Reverse Proxy auf Port 80 oder 443, der den Host-Header ausliest und an den echten Port der App weiterleitet.

Selbst gebaut: hosts-Datei plus Reverse Proxy

Sie können benannte URLs aus Bausteinen zusammensetzen, die Sie bereits kennen. Fügen Sie einen Hostnamen in /etc/hosts ein (oder verlassen Sie sich auf die automatische .localhost-Auflösung) und betreiben Sie dann einen Reverse Proxy, der den Namen auf den Port Ihres Dev-Servers abbildet. Eine Caddy-Konfiguration ist etwa so knapp, wie es nur geht:

app.localhost {
  reverse_proxy localhost:3000
}
api.localhost {
  reverse_proxy localhost:8080
}

Caddy stellt lokale TLS-Zertifikate automatisch bereit; nginx und Traefik erledigen dieselbe Aufgabe mit mehr Konfigurationsaufwand. Für lokale Wildcard-Domains kann dnsmasq einen kompletten *.test-Namensraum auf 127.0.0.1 auflösen, sodass Sie sich hosts-Einträge pro Name sparen. Und jeder Dev-Server muss weiterhin auf Host und Port festgenagelt werden (Vite über server.host und server.port, webpack über devServer), damit der Proxy ein stabiles Ziel hat.

Der Kompromiss ist der Pflegeaufwand. Sie verwalten die Proxy-Konfiguration, das Zertifikatsvertrauen, die hosts-Einträge und die Portzuweisungen pro Projekt – und halten alle vier von Hand synchron, während Services kommen und gehen. Für eine oder zwei langlebige Apps ist das in Ordnung. Über ein Monorepo hinweg wird es zu einer eigenen Pflichtaufgabe.

portless: benannte URLs, die einfach funktionieren

portless ist ein lokaler Proxy, der die gesamte Kette automatisiert. Sie stellen Ihrem Dev-Befehl ein Präfix voran, aus next dev wird also portless run next dev – oder Sie führen einfach portless aus und lassen den App-Namen aus package.json, dem Git-Root oder dem Verzeichnis ableiten. Der Proxy startet automatisch, weist einen freien Port im Bereich 4000–4999 zu, injiziert ihn über die Umgebungsvariable PORT und routet https://name.localhost dorthin. Frameworks, die PORT ignorieren – etwa Vite, Astro, Angular und Expo – erhalten das passende --port-Flag übergeben, dazu ein entsprechendes --host-Flag, wo eines nötig ist.

In den 0.15.x-Releases aktiviert portless standardmäßig HTTPS mit HTTP/2 auf Port 443 und erzeugt beim ersten Start eine lokale Zertifizierungsstelle, der es Vertrauen ausspricht. Unter macOS und Linux erhöht es die Rechte automatisch per sudo, weil das Binden von Port 443 Root-Rechte erfordert; portless trust fügt die CA nachträglich hinzu, falls Sie die Abfrage übersprungen haben. Frühere Beschreibungen mit einem optionalen --https-Flag und :1355 als Standard beziehen sich auf eine überholte Version. HTTP/2 hilft lokal aus einem bestimmten Grund: Ein Browser hält zu einem einzelnen Host nur sechs HTTP/1.1-Verbindungen offen, sodass ein Dev-Server, der hunderte einzelne, ungebündelte Dateien ausliefert, diese in eine Warteschlange stellt – während eine einzige HTTP/2-Verbindung sie alle gleichzeitig transportiert. portless benötigt Node.js 24 oder neuer.

Einige Features zahlen sich bei größeren Setups besonders aus. Subdomains wie api.myapp.localhost organisieren Microservices; eine einzige portless.json im Monorepo-Root erkennt Workspace-Pakete automatisch. Für einen Service mit festem Port, den Sie nicht ändern können – etwa einen Docker-Container – bildet portless alias <name> <port> eine benannte URL darauf ab, und PORTLESS=0 umgeht den Proxy vollständig, etwa für CI oder einen schnellen Test. Wenn Sie eine eigene TLD möchten, empfiehlt portless .test, das RFC 6761 ebenfalls reserviert, und warnt vor zwei anderen: .local kollidiert mit mDNS und Bonjour, während .dev Google gehört, das per HSTS ein erzwungenes Upgrade auf HTTPS durchsetzt.

Warum stabile lokale URLs für KI-Coding-Agents wichtig sind

KI-Coding-Agents scheitern bei Ports an derselben Sache wie Menschen, nur unbemerkt: Sie kodieren eine Nummer hart, die sie zuvor im Kontext gesehen haben, oder raten falsch. Ein Agent, der eine feste https://api.myapp.localhost aus einer AGENTS.md-Datei liest, spricht immer den richtigen Service an, statt über Sessions hinweg zwischen 3001 und 8080 zu wechseln und Sie mit Rückfragen zu unterbrechen. Das ist eine allgemeine Verschiebung im Dev-Tooling: Stabile Endpunkte sind Infrastruktur für Automatisierung. portless liefert Skill-Dateien mit, und die 0.15.x-Releases ergänzen Markdown-Dokumentationsseiten sowie einen llms.txt-Index, damit seine URLs für Agents von Anfang an auffindbar sind.

Den passenden Ansatz wählen

Nur .localhost-TLDTLD + Reverse Proxyportless
Entfernt den Port?NeinJaJa
Zusätzliche ToolsKeineCaddy/nginx/TraefikEine globale Installation
HTTPSManuellVom Proxy bereitgestelltStandardmäßig aktiv
Automatische Monorepo-ErkennungNeinNeinJa
Agent-freundlichTeilweiseTeilweiseJa (Skill-Dateien, llms.txt)
EinrichtungsaufwandAm geringstenMittel (manuelle Synchronisation)Gering

Die Entscheidung in einem Satz: Greifen Sie zur eingebauten TLD plus Reverse Proxy, wenn Sie keine neuen Tools wollen und die Konfigurationspflege nicht scheuen; greifen Sie zu portless, wenn benannte URLs über viele Services, ein Monorepo oder mit KI-Agents einfach funktionieren sollen.

Benannte, stabile, menschenlesbare lokale URLs sind Portnummern eindeutig überlegen, und Sie können sie in Minuten einführen: Legen Sie heute ein zweizeiliges Caddyfile an, oder setzen Sie einem Dev-Skript portless voran und denken Sie nie wieder an EADDRINUSE.

FAQs

Muss ich .localhost-Subdomains in meine /etc/hosts-Datei eintragen?

Nein, nicht in Chrome, Firefox oder Edge. Diese drei lösen jeden Namen unter der .localhost-TLD selbst auf 127.0.0.1 auf, weil RFC 6761 die TLD für die Loopback-Nutzung reserviert – app.localhost und api.localhost funktionieren also ohne jede Konfiguration. Safari ist die Ausnahme, da es die Auflösung an den System-DNS-Resolver übergibt und nicht jede Resolver-Konfiguration für .localhost-Subdomains antwortet. Fügen Sie dort einen /etc/hosts-Eintrag hinzu, wenn ein Name nicht lädt.

Entfernt die Verwendung einer .localhost-Domain die Portnummer meines Dev-Servers?

Nein. Die .localhost-TLD löst lediglich den Hostnamen auf 127.0.0.1 auf; Ihre App lauscht weiterhin auf ihrem ursprünglichen Port, sodass app.localhost ohne Port auf app.localhost:80 landet, wo nichts läuft. Um die Nummer tatsächlich loszuwerden, brauchen Sie einen Reverse Proxy auf Port 80 oder 443, der den Host-Header ausliest und an den echten Port der App weiterleitet. Genau das automatisieren Tools wie Caddy oder portless.

Warum lecken Cookies zwischen localhost:3000 und localhost:3001, aber nicht zwischen app.localhost und api.localhost?

Browser grenzen Cookies nach Host ab und ignorieren den Port, sodass localhost:3000 und localhost:3001 denselben Host – localhost – und damit denselben Cookie-Speicher teilen. Benannte Subdomains haben unterschiedliche Hosts, weshalb app.localhost und api.localhost getrennte Cookies behalten. Da die Same-Origin-Policy auf Schema, Host und Port basiert, trennen unterschiedliche Hostnamen auch localStorage und sessionStorage sauber – was portbasierte Origins nicht tun.

Welche Node.js-Version benötigt portless, und funktioniert es ohne sudo?

portless benötigt Node.js 24 oder neuer. Unter macOS und Linux erhöht es beim ersten Start automatisch die Rechte per sudo, weil das Binden von Port 443 für HTTPS Root-Rechte erfordert. HTTPS läuft von Haus aus mit HTTP/2, und portless erzeugt beim ersten Start eine lokale Zertifizierungsstelle und spricht ihr Vertrauen aus; verwenden Sie portless trust, um die CA nachträglich hinzuzufügen, falls Sie die erste Abfrage übersprungen haben.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.