URL здається простим, бо браузер приховує більшість деталей. Ми копіюємо посилання, змінюємо параметр пошуку або відкриваємо сторінку, майже не думаючи про граматику адреси. Насправді URL одночасно визначає ресурс, повідомляє спосіб доступу до нього, переносить додаткові параметри, переживає копіювання між системами й відрізняє службову пунктуацію від звичайного тексту. Розуміння цієї структури допомагає проєктувати маршрути, API, аналітику та безпеку без випадкових помилок.
URL є структурованою інструкцією
Розглянемо адресу https://example.com:443/products/coffee?sort=price#reviews. Схема вказує використовувати HTTPS. Хост визначає сервер, а порт — конкретну мережеву точку. Шлях описує ресурс, query передає параметри, а фрагмент позначає частину сторінки або стан, який обробляє клієнт.
Кожен роздільник має окреме значення. Заміна одного символу може перетворити частину значення на новий параметр або сегмент шляху. Тому розбирати URL простим split ненадійно: однакова пунктуація підпорядковується різним правилам у різних компонентах. Для цього потрібен повноцінний URL-парсер.
Навіщо існує percent-encoding
Безпосередньо в URL можна використовувати обмежений набір символів. Unicode, пробіли та знаки зі структурним значенням представляють через percent-encoded байти. Пробіл зазвичай стає %20, а амперсанд, який є частиною значення, — %26.
Кодування захищає сенс. Без нього пошуковий запит із амперсандом може перетворитися на два параметри, а знак питання в назві файлу — випадково почати query. Percent-encoding повідомляє парсеру, що символ є даними, а не частиною граматики адреси.
Повна адреса й окремий компонент кодуються по-різному
Якщо закодувати всю адресу як одне значення, зникне пунктуація, що створює її структуру. Якщо не закодувати динамічний компонент, користувацькі дані зможуть змінити цю структуру. Саме тому бібліотеки розрізняють кодування повного URI та окремого query value або path segment.
Найнадійніше будувати URL зі структурованих частин. URL builder отримує базову адресу, сегменти шляху й параметри окремо, кодує кожне значення у правильному контексті та зберігає потрібні роздільники. Ручна конкатенація працює лише доти, доки реальні дані не містять пробілів, слешів, плюсів, амперсандів або нелатинських символів.
Query — це не просто текст після знака питання
Query часто виглядає як пари назва-значення, розділені амперсандами, але домовленості відрізняються. Ключі можуть повторюватися, значення — бути порожніми, порядок — впливати на підпис, а дужки — означати вкладену структуру у конкретному фреймворку. Плюс іноді є плюсом, а іноді представляє пробіл.
API має документувати ці правила. Якщо фільтр приймає кілька значень, клієнт повинен знати, чи повторювати ключ або надсилати список. Для підписаних запитів потрібно точно визначити порядок і нормалізацію. Припущення, що всі query parser поводяться однаково, створює складні несумісності.
Шлях показує ієрархію, але не є шляхом на диску
Вебшлях часто нагадує каталог, проте сервер може інтерпретувати його як завгодно. Маршрут /users/42/orders описує логічний зв’язок, а не обов’язково папки. Кожен динамічний сегмент кодують окремо, бо слеш усередині значення інакше створить новий сегмент.
Нормалізація шляху має значення для безпеки. Proxy і застосунок можуть по-різному трактувати подвійні слеші, dot segments, регістр або змішане кодування. Зловмисники використовують такі розбіжності. Усі шари повинні погоджено парсити й нормалізувати адресу до авторизації та кешування.
Фрагмент зазвичай не надсилається серверу
Частина після # обробляється клієнтом. Браузер використовує її для переходу до елемента або зберігання стану SPA. У звичайному HTTP-запиті сервер фрагмента не отримує, тому backend не знайде його в логах або request object.
Це не робить фрагмент безпечним місцем для секретів. Значення може залишитися в історії браузера, потрапити на скриншот, бути прочитаним розширенням або JavaScript-кодом сторінки. Фрагмент змінює спосіб транспортування, а не чутливість даних.
URL стає довготривалим публічним інтерфейсом
Посилання виходять за межі застосунку: їх зберігають у закладках, індексують, надсилають у повідомленнях, записують у документацію та логи. Тимчасове рішення маршрутизації може перетворитися на багаторічне зобов’язання. Стабільні й описові адреси полегшують використання та зменшують кількість міграцій.
URL також стає ключем для спостереження. Послідовні форми маршрутів дозволяють агрегувати метрики без запису чутливих значень, а неконтрольовані динамічні шляхи створюють надмірну cardinality у логах і dashboard.
Доменне ім’я теж має структуру й правила
Host не є довільним текстом. Домен читається ієрархічно справа наліво, може містити subdomain і перетворювати міжнародні назви через IDNA. Відображений Unicode-домен інколи відрізняється від ASCII-форми, яку фактично використовує мережа.
Для безпеки потрібно порівнювати нормалізований host, а не шукати дозволений текст як substring. Адреса trusted.example.attacker.test не належить домену trusted.example, хоча містить знайому послідовність символів.
Відносні та абсолютні адреси вирішують різні задачі
Відносний URL зручний для внутрішньої навігації, бо успадковує схему й host поточної сторінки. Абсолютний URL потрібен у листах, sitemap, API-відповідях і документах, які існують поза контекстом браузера. Помилка вибору часто проявляється лише після переходу на інший домен або protocol.
Побудову абсолютних адрес краще централізувати й враховувати trusted proxy headers. Інакше застосунок може створювати HTTP-посилання на HTTPS-сайті або приймати підроблений host із запиту.
Практика надійної роботи з адресами
Використовуйте структуровані parser і builder, кодуйте компоненти відповідно до контексту, документуйте query conventions і нормалізуйте адресу однаково в усіх шарах. У тестах перевіряйте Unicode, пробіли, плюси, слеші, повторені параметри та порожні значення.
Для критичних інтеграцій зберігайте відомі приклади адрес і очікуваний результат парсингу як contract tests. Вони покажуть несумісність після оновлення framework або proxy.
URL є компактним текстом, але водночас одним із найважливіших інтерфейсів вебу. Коли кожен компонент має чітке значення, адреси залишаються зрозумілими для людей і передбачуваними для програм.