URL-Parser & Decoder
Parsen Sie jede URL in ihre Komponenten, Protokoll, Host, Port, Pfad, Query-Parameter und Fragment.
URL-Anatomie: Sechs Komponenten, eine lange Geschichte
Eine URL wird in sechs konzeptionelle Teile zerlegt: scheme://userinfo@host:port/path?query#fragment. Das scheme sagt dem Client, welches Protokoll zu verwenden ist (https, http, ftp, mailto, file, data) und ist der einzige immer vorhandene Teil. Die Komponente userinfo (username:password@) ist im modernen Gebrauch selten; Browser entfernen sie meist aus angezeigten URLs, weil sie seit den 1990ern ein Phishing-Vektor ist. Der host ist der Netzwerkort, ein registrierter Domainname, eine IP-Adresse (IPv4 als gepunktetes Quartett oder IPv6 in eckigen Klammern) oder ein spezieller Name wie localhost. Der port ist der TCP/UDP-Port (80 default für HTTP, 443 für HTTPS, etc.); wenn weggelassen, gilt der Default des Schemas. Der path ist die durch Schrägstriche getrennte Hierarchie, die die Ressource innerhalb des Hosts identifiziert. Der query string (alles nach dem ?) trägt durch & getrennte Schlüssel-Wert-Paare, verwendet für Filterung, Pagination, Tracking, Formular-Übermittlung. Das Fragment (alles nach #) ist der einzige Teil der URL, der nie an den Server geschickt wird, er wird vollständig clientseitig vom Browser verarbeitet, um zu einem bestimmten Abschnitt zu scrollen oder, in Single-Page-Apps, den Routen-Zustand anzuzeigen.
Das Format des Query-Strings selbst hat eine Gabelung: traditionelles ?key=value&key2=value2 mit Werten, die nach RFC 3986 percent-encodiert sind, vs die ältere form-encoded-Konvention application/x-www-form-urlencoded, bei der + ein Leerzeichen bedeutet (ursprünglich für HTML-Formular-Übermittlungen). Die meisten Parser handhaben beide, aber die Konvertierung ist asymmetrisch: %20 dekodiert immer zu einem Leerzeichen; + dekodiert nur innerhalb eines Query-Strings zu einem Leerzeichen, nie innerhalb eines Paths. Das ist einer der häufigsten URL-Parsing-Bugs in der Praxis.
Eine kurze Geschichte der URL
Die URL (ursprünglich „Universal Document Identifier“, dann „Universal Resource Locator“) wurde von Tim Berners-Lee zwischen seinem März-1989-Memo „Information Management: A Proposal“ am CERN (das, das sein Chef Mike Sendall mit „Vague but exciting“ kommentierte) und den ersten öffentlich browsbaren Webseiten vom August 1991 erfunden. Die kanonische erste URL war http://info.cern.ch/hypertext/WWW/TheProject.html, veröffentlicht am 6. August 1991. Die IETF-Diskussionen von 1992 benannten UDIs in URLs um, um einen Vokabularstreit zu umgehen. RFC 1738 („Uniform Resource Locators“), verfasst von Berners-Lee, Masinter und McCahill, wurde im Dezember 1994 als erste formale URL-Syntax veröffentlicht. RFC 2396 folgte im August 1998 und verallgemeinerte URLs zum breiteren URI-Konzept. Die aktuelle kanonische Spezifikation ist RFC 3986 („URI Generic Syntax“), veröffentlicht Januar 2005, herausgegeben von Berners-Lee, Roy Fielding und Larry Masinter, ein STD-66 Internet Standard, der höchsten Reifestufe der IETF. RFC 3986 ist das, was jeder URL-Parser nominell anvisiert. In der Praxis weichen moderne Browser in zahlreichen Edge Cases von RFC 3986 ab, weshalb die WHATWG einen separaten URL Living Standard auf url.spec.whatwg.org pflegt, der beschreibt, was Browser tatsächlich tun; die WHATWG-Spec strebt ausdrücklich an, RFC 3986 und RFC 3987 mittelfristig abzulösen, und beide weichen weiterhin bei Dingen wie der Behandlung von führenden Leerzeichen, Percent-Encoding-Sets und Unicode-Normalisierung voneinander ab.
Unreservierte, reservierte und percent-encodierte Zeichen
RFC 3986 §2.3 definiert die unreservierten Zeichen: die einzigen Zeichen, die in jeder URI-Komponente ohne Percent-Encoding garantiert sicher sind: A-Z, a-z, 0-9, Bindestrich (-), Punkt (.), Unterstrich (_) und Tilde (~). 66 Zeichen insgesamt. Alles andere ist entweder ein reserviertes Zeichen mit struktureller Bedeutung in irgendeiner Komponente (gen-delims (:/?#[]@) und sub-delims (!$&'()*+,;=)) oder „sonstig“ und muss percent-encodiert werden, wenn es in einer URI erscheint. Percent-Encoding (RFC 3986 §2.1) nimmt die Byte-Folge eines Zeichens (in UTF-8, sofern das Schema nichts anderes vorgibt) und ersetzt jedes Byte durch %HH, wobei HH der zweistellige Hex-Wert des Bytes ist. So wird ein UTF-8-encodiertes é (Bytes 0xC3 0xA9) zu %C3%A9; das russische Wort привет wird zu %D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82: zwei Bytes pro Zeichen, sechs %XX-Tripel und 36 percent-encodierte Zeichen URL für sechs kyrillische Buchstaben.
Browser zeigen percent-encodierte Paths auf zwei Arten an: die meisten modernen Browser (Chrome, Firefox, Safari) dekodieren und rendern die ursprünglichen Unicode-Glyphen in der Adressleiste, wenn die Codierung gültiges UTF-8 ist, kopieren aber die wörtliche percent-encodierte Form, wenn der Nutzer die URL kopiert. Ältere Browser und viele Web-Logs zeigen nur die percent-encodierte Form, deshalb können „hübsche Unicode-URLs“ irreführend sein: sie sehen in der Adressleiste schön aus und in jedem Text, in dem sie geteilt werden, hässlich. RFC 3987 („Internationalized Resource Identifiers“, IRIs), veröffentlicht Januar 2005, formalisierte Unicode-URLs in ihrer nicht-encodierten Form; Punycode (RFC 3492, März 2003) definiert, wie internationalisierte Domainnamen für DNS Label für Label in ASCII codiert werden: das chinesische Top-Label 中国 wird zu xn--fiqs8s, sodass example.中国 auf DNS-Ebene zu example.xn--fiqs8s wird. Die kanonische Demonstration sind Wikipedias IRI-URLs: https://ja.wikipedia.org/wiki/東京 funktioniert in jedem modernen Browser, obwohl die zugrundeliegende Anfrage den Path als /wiki/%E6%9D%B1%E4%BA%AC kodiert.
Der WHATWG-URL-Standard, was Browser tatsächlich tun
RFC 3986 der IETF sagt eine Sache; Browser tun etwas leicht anderes. Die WHATWG (das Standardisierungsgremium der Browser-Hersteller) pflegt einen separaten URL Living Standard auf url.spec.whatwg.org, der die algorithmische Zustandsmaschine beschreibt, die Browser tatsächlich ausführen, einschließlich Behandlung von führendem Whitespace, Steuerzeichen, je nach Komponente variierender Percent-Encoding-Sets und Unicode-Normalisierung. Die WHATWG-Spec ist das, was der URL-Konstruktor des Browsers (new URL(input)) implementiert, und das, worauf Node.js, Deno und Bun für ihr eingebautes URL-Parsing alle konvergiert sind. Der Ada-URL-Parser: geschrieben in C++ von Yagiz Nizipli, Daniel Lemire und anderen, wurde der WHATWG-konforme Parser, der das URL-Parsing von Node.js seit Node.js 18.16.0 (April 2023) antreibt und den älteren url.parse()-Pfad ablöst; er ist messbar schneller als jede vorherige Implementierung und der De-facto-Standard für hochperformantes URL-Parsing in 2026. RFC 3986 und die WHATWG-Spec sind nach wie vor nicht vollständig in Einklang gebracht, und historische Divergenz taucht in Legacy-Code-Pfaden und älteren Runtime-Versionen noch auf.
Der Query-String, und die URLSearchParams-API
Der Query-String ist technisch nur „alles nach dem ? und vor dem #“, die Spec definiert nicht wirklich, wie er zu interpretieren ist. Die Konvention ?key=value&key=value mit &-Trennern ist Konvention, nicht Anforderung. In der Praxis dominieren zwei Query-String-Formate: application/x-www-form-urlencoded (das Standardformat für HTML-Formular-Übermittlung, bei dem + ein Leerzeichen bedeutet) und die Standard-URI-Query-Konvention (bei der Leerzeichen immer %20 ist). Die URLSearchParams-API des Browsers (Teil des WHATWG URL Living Standard) handhabt beide Formate transparent für das Parsing und gibt beim Stringifizieren die form-encoded-Variante aus. Wiederholte Schlüssel sind legal: ?tag=red&tag=blue&tag=green ist gültig, und URLSearchParams.getAll('tag') liefert ['red', 'blue', 'green']. Verschiedene Web-Frameworks behandeln wiederholte Schlüssel unterschiedlich: Rails und Express sammeln wiederholte Schlüssel in Arrays, während PHP frühere Werte durch spätere ersetzt, sofern der Schlüssel nicht die Klammer-Konvention name[] verwendet, eine ständige Quelle von Cross-Framework-Bugs in API-Integrationen.
Häufige URL-Parsing-Stolperfallen
- Doppelkodierung. Eine bereits kodierte URL zu kodieren produziert
%2520(das%selbst wird als%25kodiert). Das taucht auf, wenn URLs durch mehrere Schichten wandern (Frontend → Backend → Analytics) und jede Schicht „hilfsbereit“ noch einmal kodiert. Die Lösung ist, ganz nach unten zu dekodieren, bevor man einmal neu kodiert. - + vs %20 in Query-Strings.
+bedeutet ein Leerzeichen innerhalb eines form-encoded-Query-Strings, bedeutet aber ein wörtliches+innerhalb eines Paths oder Fragments. Die Konventionen zu mischen produziert schwer-debugbare Bugs, bei denen „John+Doe“ in der Query zu „John Doe“ wird, im Path aber „John+Doe“ bleibt. - Groß-/Kleinschreibung. Der Host ist case-insensitiv (
EXAMPLE.comundexample.comsind derselbe Host); der Path ist auf den meisten Servern (Linux/Unix) case-sensitiv, auf anderen (Windows IIS standardmäßig, macOS HFS+) aber case-insensitiv. Das bedeutet, dieselbe URL kann je nach Server zu unterschiedlichem Inhalt aufgelöst werden. - IPv6 in URLs. IPv6-Adressen enthalten Doppelpunkte, die mit dem host:port-Trenner kollidieren. Die Lösung ist, die IPv6-Adresse in eckige Klammern zu setzen:
http://[2001:db8::1]:8080/path. Viele URL-Parser scheiterten historisch daran; moderne Browser und der WHATWG-Parser handhaben es korrekt. - OAuth-Fragment-Tokens. Der OAuth 2.0 Implicit Grant Flow gab Access Tokens im URL-Fragment zurück (
#access_token=...), damit sie nicht in Server-Logs erscheinen. Moderne OAuth-Empfehlungen raten von diesem Flow zugunsten von Authorization Code mit PKCE ab, doch Legacy-Systeme geben weiterhin Fragment-Tokens aus. - Round-Trip-Nicht-Identität. Eine URL zu parsen und wieder zu stringifizieren produziert nicht immer den ursprünglichen String, der Parser normalisiert (dekodiert percent-encodierte unreservierte Zeichen, schreibt den Host klein, sortiert Query-Parameter in einigen Implementierungen). Nimm nicht an, dass
parse(url).toString() === url.
Häufige Anwendungsfälle
- Debugging von API-Anfragen. Ein REST-Endpoint mit einem langen Query-String ist schwer zu lesen; das Parsen zeigt jeden Parameter in einer eigenen Zeile.
- Inspektion von OAuth-Callbacks. Auth-Flow-URLs tragen kodierte State, Code, Scope und Access Tokens, die zum Debuggen dekodiert werden müssen, ohne sie einem Server auszusetzen.
- Verfolgung von Weiterleitungsketten. Wenn eine URL durch mehrere Zwischen-URLs weitergeleitet wird, hilft das Parsen jeder einzelnen, der Kette zu folgen und zu identifizieren, wo eine Weiterleitung kaputt geht.
- UTM-Tag-Audit. Analytics-URLs (
?utm_source=...&utm_medium=...&utm_campaign=...) sind Parameter für Parameter leichter zu lesen als als Wand aus Query-String. - Sicherheits-Audit. Suche nach SQL-Injection-Mustern oder Path-Traversal-Sequenzen in URL-Parametern; das Parsen legt jeden Wert separat zur Überprüfung offen.
- Parsing von form-encoded Bodies. Dasselbe Format, das in URL-Query-Strings verwendet wird, wird in
application/x-www-form-urlencoded-POST-Bodies verwendet. - Inspektion von Deep Links. Mobile-App-Deep-Links und Web-App-Routen kodieren komplexen Zustand im Path oder in der Query; das Parsen macht die Struktur sichtbar.
- Affiliate-/Share-Link-Prüfung. Tracking-Links aus E-Mail-Kampagnen oder Affiliate-Programmen tragen kodierte Weiterleitungs-URLs und Tracking-IDs, die vom Dekodieren profitieren.
Datenschutz: URLs tragen echte Geheimnisse
URLs werden im Allgemeinen nicht als geheim behandelt, tragen aber oft Daten, die es sind. OAuth-Callback-URLs enthalten Access Tokens. Magic-Link-Login-URLs enthalten Einmal-Authentifizierungs-Tokens. Passwort-Reset-Links enthalten Reset-Tokens. Interne API-URLs enthalten interne Hostnamen und Routing-Pfade, die Infrastruktur preisgeben. Sogar gewöhnliche Anwendungs-URLs verraten Nutzerverhalten durch Query-Parameter, Suchbegriffe, Filterauswahlen, Profil-IDs, Session-Identifier. Der Referer-Header leakt die vorherige URL an jede verlinkte Site, gemindert durch den 2017 als W3C Candidate Recommendation eingeführten Referrer-Policy-Header (Browser-Defaults variieren weiterhin). URLs landen in Server-Access-Logs, im Browser-Verlauf, in Browser-Bookmarks, in CDN-Logs, in Analytics-Pipelines, in Chat-App-Link-Vorschauen. Ein serverseitiger URL-Parser sieht jede URL, die in ihn eingefügt wird; ein Nur-Browser-Parser nicht. Für interne API-URLs, OAuth-Callbacks mit Tokens, Passwort-Reset-Links oder jede URL, die du nicht auf der Festplatte eines Fremden haben möchtest, ist ein Nur-Browser-Parser die richtige Architektur. Überprüfe es im Network-Tab der DevTools, während du parst, oder nimm die Seite nach dem Laden offline (Flugmodus).
Häufig gestellte Fragen
Welche Teile enthält eine URL?
Sechs konzeptionelle Teile: scheme (https, http, ftp, mailto), userinfo (im modernen Gebrauch selten, meist von Browsern als Phishing-Mitigation entfernt), host (Domain oder IP), port (Default 80 für HTTP, 443 für HTTPS), path (durch Schrägstriche getrennte Hierarchie), query (Schlüssel-Wert-Paare nach ?) und fragment (nach #, nie an den Server gesendet). Die vollständige Grammatik steht in RFC 3986 §3 (Januar 2005, STD 66) und im WHATWG URL Living Standard.
Wie dekodiere ich URL-encodierte Zeichen?
Percent-Encoding ersetzt unsichere Zeichen durch ein % gefolgt vom Hex-Code des Bytes: ein Leerzeichen ist %20, ein Doppelpunkt %3A, ein Schrägstrich %2F, ein Ampersand %26, das At-Zeichen %40. UTF-8-Multibyte-Zeichen werden Byte für Byte kodiert, also wird é zu %C3%A9 (zwei Bytes). Der Parser dekodiert in der angezeigten Ausgabe automatisch alle percent-encodierten Zeichen. Die JavaScript-Standardfunktionen sind encodeURIComponent() zum Kodieren einzelner Werte und decodeURIComponent() zum Dekodieren.
Was ist ein URL-Fragment?
Das Fragment (alles nach #) ist der einzige Teil der URL, der vollständig clientseitig verarbeitet wird, er wird nie in HTTP-Anfragen an den Webserver gesendet. Ursprünglicher Zweck: den Browser zu einem Anker-Element mit dieser ID zu scrollen. Moderne Verwendungen umfassen Routen-Zustand von Single-Page-Applications (#/dashboard/profile), OAuth-Implicit-Flow-Tokens (heute zugunsten von Authorization Code mit PKCE nicht mehr empfohlen) und PDF-Seitennavigation (file.pdf#page=5). Weil Fragmente den Server nicht erreichen, sind sie ein Ort, um Werte zu verstecken, die nicht in Server-Logs auftauchen sollten.
Warum bedeutet + manchmal Leerzeichen und manchmal +?
Es existieren zwei Encoding-Konventionen. application/x-www-form-urlencoded (das Standardformat für HTML-Formular-Übermittlung) kodiert Leerzeichen als +; Standard-Percent-Encoding (nach RFC 3986) kodiert Leerzeichen als %20. Beides ist in Query-Strings gültig; in Paths und Fragmenten ist nur %20 gültig. URLSearchParams handhabt beides transparent. Der Cross-Context-Bug entsteht, wenn Code encodeURIComponent (das Leerzeichen als %20 kodiert) für Query-Parameter verwendet, die der Server in form-encoded erwartet, oder umgekehrt.
Werden relative URLs unterstützt?
Der Parser erwartet eine vollständige URL mit Schema. Für einen relativen Path wie /api/users stelle eine Basis-URL voran (https://example.com/api/users), um ihn zu parsen. Etwas Relative-URL-Parsing (Auflösung gegen eine Basis-URL, wie der Browser es für href-Attribute macht) steht auf der Roadmap, die Zwei-Argument-Form des WHATWG-URL-Konstruktors (new URL(relative, base)) handhabt das und ist das, was Produktions-Code verwenden sollte.
Werden meine URLs irgendwohin gesendet?
Nein. Das Parsen läuft vollständig in deinem Browser über den WHATWG-URL-Konstruktor, die URL, die du einfügst, verlässt dein Gerät nie. Überprüfe es im Network-Tab der DevTools, während du auf Parse klickst, oder nimm die Seite nach dem Laden offline (Flugmodus). Sicher für OAuth-Callback-URLs mit Access Tokens, Passwort-Reset-Links mit Einmal-Tokens, interne API-URLs, die Infrastruktur preisgeben, oder jede URL, die du nicht auf der Festplatte eines Fremden haben möchtest.