Кодирование и декодирование URL: полное руководство
Как работает процентное кодирование URL, какие символы нужно кодировать, в чём разница между encodeURIComponent и encodeURI, и когда кодирование URL важно для безопасности.
URL выглядят просто снаружи — строка текста, указывающая на ресурс. Но под капотом они подчиняются строгой грамматике, допускающей лишь определённый набор символов. Стоит попытаться передать в URL пробел, амперсанд или символ не из ASCII без кодирования — и возникают ошибки, которые бывает сложно отладить. Процентное кодирование (URL-кодирование) — это механизм, позволяющий безопасно встраивать произвольные данные в URL.
Вы можете мгновенно кодировать и декодировать URL с помощью кодировщика/декодировщика URL BrowseryTools — бесплатно, без регистрации, всё остаётся в браузере.
Почему специальные символы ломают URL
Спецификация URL (RFC 3986) резервирует определённые символы для структурных целей. Знак ? отделяет путь от строки запроса. Знак & разделяет параметры запроса друг от друга. Знак # обозначает идентификатор фрагмента. Знак / разделяет сегменты пути. Если ваши данные содержат любой из этих символов, парсер URL не сможет отличить ваши данные от структуры самого URL.
Рассмотрим поисковый запрос rock & roll. При наивном построении URL получается:
/search?q=rock & roll
^ ^
| └── похоже, здесь начинается новый параметр
└── этот & делит q и мнимый второй параметрПарсер читает q=rock (с пробелом в конце) как первый параметр, затем встречает то, что похоже на начало второго параметра с именем roll. Оба значения неверны. Правильный URL: /search?q=rock%20%26%20roll — пробел становится %20, а амперсанд — %26.
Как работает процентное кодирование
Процентное кодирование преобразует байт в трёхсимвольную последовательность: знак процента и две заглавные шестнадцатеричные цифры, представляющие значение байта. Символ пробела (байт ASCII 32, шестнадцатеричный 0x20) становится %20. Символ «собачки» (@, ASCII 64, 0x40) становится %40. Правило:
percent-encode(byte) = "%" + byte.toString(16).toUpperCase().padStart(2, "0") Примеры: пробел (0x20) → %20 @ (0x40) → %40 [ (0x5B) → %5B € (UTF-8: 0xE2 0x82 0xAC) → %E2%82%AC
Для многобайтовых Unicode-символов (всё, что выходит за пределы ASCII) символ сначала кодируется в байты UTF-8, а затем каждый байт кодируется процентным кодированием. Знак евро € — это три байта UTF-8, поэтому он превращается в три закодированных последовательности: %E2%82%AC.
Безопасные символы и зарезервированные символы
Не каждый символ нуждается в кодировании. RFC 3986 определяет два набора, безопасных для использования как есть:
- Незарезервированные символы — A–Z, a–z, 0–9, дефис, подчёркивание, точка, тильда. Они не имеют специального смысла и никогда не требуют кодирования.
- Зарезервированные символы —
: / ? # [ ] @ ! $ & ' ( ) * + , ; =. Они безопасны в своих структурных позициях, но должны кодироваться при появлении в значениях данных.
Всё остальное — пробелы, Unicode, управляющие символы, большинство знаков препинания — должно всегда кодироваться.
encodeURI против encodeURIComponent: критическое различие
JavaScript поставляется с двумя встроенными функциями кодирования, и путаница между ними является одной из самых распространённых ошибок URL-кодирования в веб-приложениях.
encodeURI() предназначен для кодирования полного URL. Он оставляет все зарезервированные символы нетронутыми, поскольку они структурно значимы в полном URL. Используйте его, если у вас есть полный URL, который может содержать пробелы или Unicode, но имеет правильную структуру:
encodeURI("https://example.com/search?q=hello world&lang=en")
// → "https://example.com/search?q=hello%20world&lang=en"
// ✓ пробел закодирован, & и ? остались нетронутымиencodeURIComponent() предназначен для кодирования одного значения — значения параметра запроса, сегмента пути, любых данных. Он кодирует и зарезервированные символы, включая &, =, ? и /:
encodeURIComponent("rock & roll")
// → "rock%20%26%20roll"
// ✓ & закодирован — безопасно использовать как значение параметра запроса
encodeURIComponent("https://example.com/page")
// → "https%3A%2F%2Fexample.com%2Fpage"
// ✓ двоеточия и слэши закодированы — безопасно как значение redirect_uriПрактическое правило: при построении URL используйте encodeURIComponent() для каждого отдельного значения параметра, но не для всего URL. Используйте encodeURI()только для полного URL, который нужно нормализовать. В современном коде предпочитайте API URL и URLSearchParams вместо ручного кодирования — они обрабатывают кодирование автоматически и корректно.
Подводные камни кодирования строки запроса
При кодировании строк запроса регулярно встречаются несколько тонких багов. Знак + заслуживает особого внимания: в формате application/x-www-form-urlencoded (формат, в котором HTML-формы отправляют данные) пробел кодируется как +, а не %20. Это устаревшее соглашение, предшествующее RFC 3986. Если бэкенд декодирует URL по правилам form-encoding, а фронтенд отправляет %20 — всё работает. Но если фронтенд отправляет +, а бэкенд декодирует по правилам RFC 3986, то + остаётся буквальным знаком плюс, а не пробелом.
// URLSearchParams использует application/x-www-form-urlencoded (+ для пробелов)
new URLSearchParams({ q: "rock & roll" }).toString()
// → "q=rock+%26+roll"
// encodeURIComponent использует RFC 3986 (%20 для пробелов)
"q=" + encodeURIComponent("rock & roll")
// → "q=rock%20%26%20roll"
// Оба варианта правильны — главное, чтобы оба конца были согласованыКак данные формы кодируются в URL
Когда HTML-форма отправляется с method="GET", браузер сериализует поля формы в строку запроса, используя application/x-www-form-urlencoded. Каждое имя и значение поля кодируется (пробелы как +, специальные символы как %XX), а поля объединяются через &. Для форм с method="POST" без атрибута enctype используется то же кодирование, но данные помещаются в тело запроса, а не в URL.
Это также формат, который fetch() использует при передаче объекта URLSearchParams в качестве тела, и это то, что большинство серверных фреймворков автоматически декодируют при чтении отправок форм.
Base64 в URL
Стандартный Base64 использует + и / — оба символа имеют специальный смысл в URL. Когда Base64-кодированные данные должны появляться в URL (распространённый паттерн для токенов, данных изображений или криптографических подписей), вместо них используйте вариант Base64URL. Он заменяет + на -, а / на _, создавая строку, безопасную в любой позиции URL без дополнительного кодирования. JWT использует этот формат для сегментов заголовка и полезной нагрузки.
Реальные баги с кодированием
Несколько паттернов багов, встречающихся в продакшн-приложениях:
- Двойное кодирование — кодирование уже закодированного URL.
%20становится%2520, потому что сам%кодируется в%25. Всегда проверяйте, не закодировано ли значение уже, прежде чем кодировать его снова. - Отсутствие encodeURIComponent для redirect_uri — OAuth-потоки передают
redirect_uriкак параметр запроса. Если он содержит?или&и не закодирован, сервер авторизации разбирает эти символы как часть структуры внешнего URL, нарушая редирект. - Не-UTF-8 кодирование — старые системы или неправильно настроенные серверы иногда кодируют строки, используя ISO-8859-1 вместо UTF-8. Байтовые последовательности для
éразличаются. Всегда единообразно используйте UTF-8 с обеих сторон. - Логирование сырых URL — запись URL, содержащего закодированные пользовательские данные, может давать вводящие в заблуждение логи, если ваш просмотрщик логов автоматически декодирует процентные последовательности, скрывая то, что реально было передано по сети.
Мгновенное кодирование и декодирование URL
Отлаживаете ли вы OAuth-редирект, вручную строите строку запроса, проверяете сломанный запрос к API или просто хотите понять, что скрывается за процентно-закодированным URL — кодировщик/декодировщик URL BrowseryTools обработает это мгновенно. Вставьте строку, выберите кодирование или декодирование, и сразу увидите результат. Без серверных запросов, без регистрации.
Бесплатный кодировщик/декодировщик URL — работает на 100% в браузере
Открыть кодировщик URL →Try the Tools — 100% Free, No Sign-Up
Everything runs in your browser. No uploads. No accounts. No ads.
Explore All Tools →