Зачем URL нужно кодирование
URL — это одновременно удобочитаемый адрес и структурированная строка, которую разбирают браузеры, прокси и серверы. Некоторые символы разделяют компоненты: ? начинает query, & разделяет параметры, # открывает fragment, / делит сегменты пути, а пробелы неудобны во многих контекстах. Если значение параметра само содержит & или =, парсер неверно прочитает структуру, если эти байты не записаны в безопасном виде.
URL-кодирование—формально percent-encoding—заменяет небезопасные или зарезервированные байты на % и две шестнадцатеричные цифры. Байт space часто становится %20 (или + в варианте application/x-www-form-urlencoded). Это не шифрование; это транспортная запись, чтобы структура и полезная нагрузка могли сосуществовать.
Попробуйте закодировать фразу с помощью инструмента «текст → URL-encoded».
Reserved и unreserved символы
RFC 3986 определяет символы unreserved, которые обычно пишутся как есть: буквы, цифры и -._~. Символы reserved вроде :/?#[]@!$&'()*+,;= имеют роли в грамматике URL. Нужно ли кодировать reserved-символ, зависит от места. / в пути — разделитель; / внутри данных одного сегмента пути может потребовать кодирования, если нужен литеральный слэш в значении сегмента.
Практическое правило: кодируйте данные, которые вставляете в компонент, так чтобы reserved-символы потеряли особое значение для окружающего парсера.
Механика percent-encoding
- Представить строку как байты (в современном вебе почти всегда UTF-8).
- Для каждого байта, который нужно закодировать, записать
%плюс hex в верхнем или нижнем регистре (декодеры принимают оба; эмиттеры часто используют верхний). - Оставить безопасные символы без изменений.
Пример: пробел в hello world становится hello%20world. UTF-8-кодировка é — байты C3 A9, поэтому символ становится %C3%A9.
Декодирование обращает отображение. Неполные последовательности вроде %Z или завершающий % следует отклонять или обрабатывать по правилам вашей платформы—не угадывайте молча в коде, чувствительном к безопасности.
Query-строки и application/x-www-form-urlencoded
HTML-формы традиционно кодируют пробелы как + и используют & / = как разделители. Библиотеки encodeURIComponent и URLSearchParams расходятся в пограничных случаях. При отладке:
- Убедитесь, означает ли
+пробел или литеральный плюс. - Кодируйте значения до объединения через
&. - Не кодируйте весь URL вслепую—у схемы и хоста другие правила.
encodeURI и encodeURIComponent в JavaScript кодируют разные наборы символов; неверный выбор — частая причина битых ссылок.
Пути, матрицы и фрагменты
Сегменты пути должны кодировать пробелы и большинство reserved-символов, не предназначенных быть разделителями.
Фрагменты (#...) обрабатываются клиентом и не отправляются серверам в HTTP-запросе; кодирование всё равно важно для корректности на клиенте.
Userinfo (user:pass@) устарел для секретов в URL—не кладите учётные данные в URL независимо от кодирования.
Двойное кодирование и ошибки декодирования
Двойное кодирование возникает, когда %20 кодируется снова в %2520. Симптомы: литеральный %20 в тексте UI или «файл не найден», потому что сервер ищет неверное имя. Недокодирование оставляет & внутри значения и обрезает последующие параметры.
Надёжный конвейер кодирует ровно один раз на границе, где сырая строка становится частью компонента URL, и декодирует один раз при извлечении значений.
Интернационализированные доменные имена и пути
Хосты используют IDNA (Punycode) для не-ASCII меток домена—это другой механизм, чем percent-encoding данных пути и query. Пути и query используют UTF-8 percent-encoding. Смешение устаревших системных кодировок (Latin-1 с одной стороны, UTF-8 с другой) даёт mojibake после декодирования.
Всегда документируйте UTF-8 как байтовую кодировку для новых API.
Аспекты безопасности
URL-кодирование не заменяет HTML-экранирование, параметризацию SQL или безопасность аргументов командной строки. Строка, корректно percent-кодированная для query-параметра, всё ещё может быть опасна, если позже попадёт в HTML без экранирования или в shell без корректного quoting.
Проблемы open-redirect и SSRF часто связаны с URL как данными. Проверяйте схемы и хосты после декодирования—атакующие могут закодировать точки, слэши или учётные данные, чтобы запутать наивные фильтры. Сравнивайте декодированные формы внимательно; ещё лучше — парсите стандартной URL-библиотекой и сверяйте свойства по allowlist.
Логирование и конфиденциальность
Query-строки часто содержат поисковые термины, токены или персональные данные. Кодирование не скрывает их от серверных логов и истории браузера. Предпочитайте тела POST или заголовки для чувствительных значений и удаляйте секреты из access-логов.
Чеклист тестирования
- Закодировать строку с пробелами,
&,=и не-ASCII символами. - Декодировать и сравнить с исходными code points.
- Поместить закодированное значение в реальный query и прочитать его обратно через parameter API фреймворка.
- Проверить, что фреймворк не декодирует дважды.
- Убедиться, что поведение
+versus%20соответствует используемому media type.
Инструмент URL-кодирования помогает с шагами 1–2 при ручном исследовании.
Типичные сценарии
Сборка поисковых ссылок. Кодируйте значение пользовательского query; не кодируйте сам ?q=.
OAuth redirect URI. Может требоваться точное совпадение строки; различия кодирования ломают redirect.
Ключи object storage в URL. Кодируйте сегменты пути, чтобы вложенные ключи с пробелами работали.
CSV со ссылками. Следите, чтобы табличные программы не переинтерпретировали последовательности %.
Связанные кодировки
Не путайте percent-encoding с Base64 или HTML entities. Base64 использует другой алфавит и предназначен для произвольных байтов в текстовых контекстах; HTML entities защищают структуру документа в разметке. Используйте энкодер, соответствующий принимающему парсеру.
Итог
URL-кодирование позволяет произвольному тексту ехать внутри компонентов URL, не ломая разделители. Преобразуйте в байты UTF-8, percent-кодируйте небезопасные значения один раз и декодируйте один раз на выходе. Осторожно выбирайте API для query versus полного URI, проверяйте URL после разбора ради безопасности и помните: кодирование — это орфография, а не конфиденциальность.