URL jest jednocześnie formatem dla maszyn i elementem interfejsu dla ludzi. Przeglądarka rozkłada go na ściśle zdefiniowane części, a użytkownik ocenia domenę, kopiuje link i oczekuje, że zadziała również za rok. Jedna linia tekstu opisuje protokół, serwer, zasób, parametry i lokalny stan dokumentu. Traktowanie URL jako struktury zamiast dowolnego stringa poprawia routing, bezpieczeństwo, cache i utrzymanie produktu.

Scheme określa pierwszy sposób interpretacji

Część przed dwukropkiem, na przykład https, mailto albo ftp, mówi klientowi, jak obsłużyć resztę. HTTPS oznacza połączenie sieciowe z TLS, natomiast mailto może otworzyć program pocztowy. Ten sam tekst po dwukropku nie ma uniwersalnego znaczenia.

Walidacja pola URL powinna więc ustalać dozwolone schematy. Pole strony WWW zwykle akceptuje HTTPS, ale nie powinno bezrefleksyjnie przepuszczać javascript:, lokalnych plików czy wewnętrznych protokołów.

Authority wskazuje cel połączenia

Po // znajduje się authority, najczęściej host oraz opcjonalny port. Host wpływa na certyfikat, cookies, same-origin i CORS. Jawny port może kierować do zupełnie innej usługi na tej samej maszynie.

Stare formaty pozwalają też na userinfo przed znakiem @. Link trusted.example@evil.test prowadzi do hosta po prawej stronie. Kontrola bezpieczeństwa musi używać parsera, a nie wyszukiwać zaufanej nazwy w tekście.

Domena wymaga porównania całych etykiet

Proste endsWith("example.com") może zaakceptować notexample.com. Allowlista powinna sprawdzać granice etykiet i świadomie decydować, czy dozwolone są subdomeny. Nazwy międzynarodowe przechodzą przez reguły IDNA, a podobne wizualnie znaki mogą wspierać phishing.

W przypadku requestów wykonywanych przez serwer sam host może nie wystarczyć. DNS może wskazać sieć prywatną. Ochrona przed SSRF obejmuje również resolver, wynikowy adres IP i politykę sieciową.

Path jest publiczną umową routingu

Ścieżka przypomina hierarchię katalogów, lecz współczesny framework może mapować ją na controller, bazę lub generowany widok. /articles/base64 nie musi odpowiadać plikowi o takiej nazwie.

Opublikowana ścieżka staje się bookmarkiem i wynikiem wyszukiwania. Zmiana klas lub folderów nie powinna automatycznie zmieniać publicznego adresu. Jeżeli nowy URL jest konieczny, poprzedni potrzebuje redirectu.

Segment path to nie cała ścieżka

Ukośnik oddziela segmenty. Wartość wstawiana jako pojedyncze ID musi zostać zakodowana funkcją przeznaczoną dla segmentu, nie dla całego URL. Zakodowanie kompletnej ścieżki mogłoby ukryć również strukturalne ukośniki.

Encoded slash jest traktowany różnie przez proxy i frameworki. Warstwa autoryzacji oraz router muszą widzieć ten sam znormalizowany path, inaczej powstaje możliwość obejścia kontroli.

Query opisuje parametry widoku lub operacji

Po znaku zapytania trafiają filtry, wyszukiwanie, sortowanie, paginacja i tracking. Ampersand rozdziela parametry, a równość nazwę od wartości. Te znaki jako dane wymagają właściwego encodingu.

URLSearchParams lub odpowiednik języka obsługuje Unicode, puste wartości i powtórzenia lepiej niż konkatenacja. API nadal powinno określić, czy zduplikowany parametr jest listą czy błędem.

Fragment pozostaje zwykle po stronie klienta

Część po # wskazuje sekcję dokumentu lub stan aplikacji frontendowej. Przeglądarka nie wysyła jej w zwykłym request HTTP. Backend nie może więc używać fragmentu do autoryzacji ani oczekiwać go w logach.

Anchory są użyteczne dla nawigacji i dostępności. Nie tworzą jednak osobnego zasobu z perspektywy serwera.

Percent-encoding oddziela dane od składni

Znak zarezerwowany może wystąpić jako wartość, jeśli jego bajty zostaną zapisane jako sekwencje procentowe. Unicode jest najpierw zamieniany na bajty, zwykle UTF-8, a następnie odpowiednie bajty otrzymują zapis typu %20.

Reguły różnią się między path i query. Spacja formularzowa może stać się plusem, podczas gdy segment używa %20. Jedna globalna funkcja escape nie zna kontekstu.

Relative URL potrzebuje zaufanej bazy

../account nie ma samodzielnego celu. Znaczenie zależy od adresu bazowego i ewentualnego elementu base. Link w e-mailu zwykle potrzebuje pełnej postaci, ponieważ klient nie zna kontekstu aplikacji.

Przy walidacji redirectu najpierw należy rozwiązać referencję względem zaufanej bazy, a potem sprawdzić końcowy absolutny URL. Sam tekst względny może ukrywać inny rezultat.

Normalizacja jest polityką, nie kosmetyką

Host nie rozróżnia wielkości liter, path może ją rozróżniać. Defaultowy port można usunąć, ale trailing slash bywa osobnym zasobem. Różne zapisy procentowe mogą prowadzić do tych samych bajtów. Nie istnieje bezpieczne uniwersalne „czyszczenie”.

Najpierw trzeba sparsować i zwalidować komponenty, a potem zastosować własną postać kanoniczną. Cache, podpisy i SEO muszą korzystać z tej samej reguły.

URL nie jest dobrym miejscem dla sekretu

Adresy trafiają do historii, proxy, analytics, screenshotów i Referer. Token w query może rozprzestrzenić się mimo HTTPS. Credentials powinny korzystać z chronionych nagłówków lub body.

Linki resetujące są świadomym wyjątkiem i potrzebują wysokiej entropii, krótkiej ważności, jednej funkcji oraz usunięcia tokena z adresu po użyciu.

Generowanie absolutnych URL wymaga zaufanej konfiguracji proxy

Aplikacja za load balancerem może odczytywać scheme i host z nagłówków przekazanych przez klienta. Jeśli każdy proxy nie jest jawnie oznaczony jako zaufany, atakujący może wpłynąć na link w e-mailu, redirect lub callback. Base URL najlepiej brać z konfiguracji produktu albo z poprawnie skonfigurowanego łańcucha trusted proxies.

Testy powinny obejmować produkcyjny układ HTTPS termination. Lokalnie aplikacja może widzieć HTTP, podczas gdy publiczny adres musi pozostać HTTPS.

URL jest długowiecznym kontraktem

Scheme i authority określają partnera komunikacji, path zasób, query wariant widoku, a fragment lokalne miejsce. Parser i builder zachowują te granice lepiej niż operacje na stringach.

Stabilny URL pozostaje udostępnialny, czytelny host buduje zaufanie, a kanoniczna forma ogranicza duplikaty. Adres jest więc częścią architektury produktu, nie tylko szczegółem transportu.