URL vs URI: почему половина инженеров путается

Знаете что забавно? Я работал с людьми, которые деплоили в production kubernetes-кластеры, настраивали service mesh и разбирали инциденты с миллионными убытками — но когда дело доходило до URL и URI, начинали мяться. «Ну, это типа одно и то же…» Не одно и то же. И разница не академическая — она влияет на то, как вы проектируете API, как настраиваете ingress и как читаете логи.

URI — это идентификатор. Любой. URI (Uniform Resource Identifier) — это просто строка, которая что-то идентифицирует. Не обязана быть адресом. Не обязана быть кликабельной. Не обязана вести куда-то в сети. urn:isbn:978-5-04-194852-3 — это URI. Идентифицирует книгу по ISBN. Но вы же не введёте это в браузер. mailto:devops@example.com — тоже URI. Идентифицирует ящик. URI — это зонтик. Общее понятие. А под зонтиком стоят конкретные форматы.

URL — это адрес. Подвид URI. URL (Uniform Resource Locator) — это URI, который не просто идентифицирует, а говорит, где найти ресурс и как к нему обратиться. https://api.example.com/v1/payments/42 — это URL. Здесь есть схема (https), хост (api.example.com), путь (/v1/payments/42). Браузер или curl знают, что с этим делать. Каждый URL — это URI. Но не каждый URI — это URL. Как каждый квадрат — прямоугольник, но не каждый прямоугольник — квадрат.

URN — третий, которого все забыли URN (Uniform Resource Name) — это тоже URI, но без адреса. Только имя. Тот urn:isbn:... выше — это URN. Он говорит «вот книга», но не говорит «где её скачать». Получается простая схема: URI делится на URL (где найти) и URN (как назвать). Всякий URL и всякий URN — это URI.

Почему это важно на практике Казалось бы, какая разница? А вот: API-шлюзы (Gravitee, Kong, Nginx, Yandex API). Когда вы настраиваете routing rules, вы матчите пути. Путь /v1/payments/{id} — это часть URL, не URI. Если в логах вы видите полный URI, а в ingress-правилах — только путь, важно понимать, что вы сравниваете. Логи и трейсинг. В OpenSearch или Kibana вы ищете по URL — потому что вам нужен конкретный запрос к конкретному эндпоинту. В метриках Prometheus вы часто видите http_requests_total{path="/api/v1/users"} — это путь URL, не URI целиком. Redirect и rewrite. В Nginx rewrite работает с URI, а return 301 https://... возвращает URL. Понимание разницы спасает от бесконечных циклов редиректов. Документация API. Когда в Swagger написано GET /v1/orders/{id} — это URI, не URL. Без базы (scheme + host) это не полный адрес. Многие этого не понимают и удивляются, почему curl ругается.

Короче URI — идентификатор. Что-то. Любое. URL — адрес. Где найти и как достучаться. URN — имя. Без адреса. URL ⊂ URI. URN ⊂ URI. При этом URL и URN могут пересекаться — один и тот же URI способен быть одновременно и локатором, и именем. RFC 3986 прямо это допускает: «A URI can be further classified as a locator, a name, or both».

Если вам это объясняет ваш тимлид на онбординге — у вас хороший тимлид. Если он сам путает — ну, вы теперь знаете больше =)