URL кодирование: percent-encoding explained
Что такое URL encoding, зарезервированные символы, как кодировать/декодировать URL, частые ошибки.
Посчитайте прямо здесь: Text → URL Encoded
Открыть инструмент целиком →Введение
URL (Uniform Resource Locator) — это адрес ресурса в сети. Чтобы URL мог передаваться по протоколу HTTP и корректно обрабатываться серверами, браузерами и прокси, он должен состоять из ограниченного набора символов ASCII. Но что делать, если в адресе нужно передать кириллицу, пробел или спецсимвол? Именно для этого существует URL кодирование, также известное как percent-encoding. В этой статье разберём, как оно устроено, какие символы считаются зарезервированными, какие ошибки чаще всего совершают разработчики и как не наступить на грабли с Unicode.
Для практической работы используйте наши инструменты: URL кодировщик и URL декодер.
Что такое URL кодирование
URL кодирование (percent-encoding) — это способ представления произвольных символов в URL с помощью знака процента и двух шестнадцатеричных цифр. Например, пробел превращается в%20, восклицательный знак — в %21, а русская буква «П» в UTF-8 — в %D0%9F. Так сервер и браузер однозначно понимают, какой байт имелся в виду, даже если сам символ не входит в набор ASCII.
Стандарт percent-encoding описан в RFC 3986. Он определяет, какие символы можно использовать в URL «как есть», а какие обязательно экранировать. Цель — избежать двусмысленности: например, символ «/» является разделителем сегментов пути, и если он встречается внутри значения параметра, его необходимо закодировать как %2F.
Незарезервированные и зарезервированные символы
Все символы в URL делятся на две большие категории: unreserved (можно использовать без экранирования) и reserved (имеют специальное значение и требуют осторожности).
Незарезервированные символы (unreserved)
К ним относятся заглавные и строчные буквы латиницы (A–Z, a–z), цифры (0–9) и четыре спецсимвола: дефис (-), точка (.), подчёркивание (_) и тильда (~). Эти символы никогда не нужно кодировать — они безопасны в любой части URL.
Зарезервированные символы (reserved)
Эти символы имеют специальное значение в синтаксисе URL: они разделяют сегменты, отделяют query-часть, обозначают параметры и т. д. К ним относятся:
| Символ | Код | Назначение |
|---|---|---|
: | %3A | Разделитель схемы и порта |
/ | %2F | Разделитель сегментов пути |
? | %3F | Начало query-строки |
# | %23 | Начало фрагмента |
[ ] | %5B %5D | IPv6-адрес |
@ | %40 | Разделитель userinfo |
! | %21 | Sub-delimiter |
$ | %24 | Sub-delimiter |
& | %26 | Разделитель параметров |
' | %27 | Sub-delimiter |
( ) | %28 %29 | Sub-delimiter |
* | %2A | Sub-delimiter |
+ | %2B | Sub-delimiter / пробел в form-data |
, | %2C | Sub-delimiter |
; | %3B | Sub-delimiter |
= | %3D | Разделитель ключа и значения |
Если зарезервированный символ нужно передать как литерал (например, в значении параметра встречается слэш), его нужно обязательно закодировать. Иначе парсер URL истолкует его как синтаксическую конструкцию.
Как кодируются не-ASCII символы
URL изначально рассчитан на ASCII. Все символы вне этого набора — кириллица, иероглифы, эмодзи — должны быть сначала преобразованы в байты по какой-то кодировке (стандарт требует UTF-8), а затем каждый байт записывается как %XX.
Например, слово «привет» в UTF-8 состоит из 12 байтов:
привет → D0 BF D1 80 D0 B8 D0 B2 D0 B5 D1 82
→ %D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82В JavaScript эту работу делает функция encodeURIComponent:
encodeURIComponent('привет мир');
// "%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%BC%D0%B8%D1%80"
encodeURIComponent('search=test&lang=ru');
// "search%3Dtest%26lang%3Dru"encodeURIComponent vs encodeURI: в чём разница
В JavaScript есть две функции для URL кодирования, и их путают чаще, чем что-либо другое. Разница принципиальная:
encodeURI— кодирует строку целиком, оставляя без изменений зарезервированные символы, которые формируют структуру URL (:/?#&=+и др.). Используется, когда у вас уже есть готовый URL и вы хотите убедиться, что в нём нет не-ASCII символов.encodeURIComponent— кодирует строку как значение параметра, экранируя все зарезервированные символы. Используется при сборке query-части URL из отдельных значений.
const url = 'https://example.com/search?q=' +
encodeURIComponent('тест & пример') +
'&lang=' + encodeURIComponent('ru');
console.log(url);
// "https://example.com/search?q=%D1%82%D0%B5%D1%81%D1%82%20%26%20%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80&lang=ru"Золотое правило: собирайте URL из частей через encodeURIComponent, а encodeURI используйте только в редких случаях, когда нужно пропустить целую готовую ссылку через «фильтр безопасности».
application/x-www-form-urlencoded
При отправке HTML-формы методом POST с типом application/x-www-form-urlencodedбраузер использует немного иной вариант кодирования: пробелы заменяются не на%20, а на знак плюса (+). Это историческое наследие — так было удобнее в ранних реализациях CGI. Современные API этот нюанс учитывают: функцияdecodeURIComponent не превращает «+» обратно в пробел, для этого нужно использовать специальные функции парсинга тела запроса.
// Форма отправляет: q=hello+world&lang=ru
// На сервере (Node.js / Express):
app.use(express.urlencoded({ extended: true }));
// req.body.q === "hello world"Частые ошибки и как их избегать
1. Двойное кодирование
Если взять уже закодированную строку и пропустить её через encodeURIComponentещё раз, символ «%» превратится в %25, и при декодировании данные исказятся. Например, %20 станет %2520, а после декодирования — «%20» вместо пробела.
2. Кодирование всего URL одним вызовом
// ОШИБКА: кодирует даже схему и хост
const bad = encodeURI('https://example.com/search?q=привет');
// "https://example.com/search?q=%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82"
// ПРАВИЛЬНО: кодируем только значение параметра
const good = 'https://example.com/search?q=' + encodeURIComponent('привет');3. Путаница с пробелом: %20 или +
В пути URL пробел должен кодироваться как %20. Знак «+» имеет смысл только в query-части и только в контексте form-urlencoded. Многие браузеры и библиотеки это понимают, но при ручном кодировании легко ошибиться. Безопаснее всегда использовать%20.
4. Незакодированные спецсимволы в путях
Если в имени файла или папке встречается «#», «?», «&» или «=», сервер воспримет их как синтаксические разделители. Например, путь /files/a&b.pdf будет разобран как путь /files/a плюс параметр b.pdf. Правильно:/files/a%26b.pdf.
URL кодирование и SEO
Поисковые системы, включая Yandex, корректно индексируют URL с percent-encoding, но предпочитают «читаемые» адреса. Современные CMS автоматически преобразуют заголовки в slug-формат: кириллица транслитерируется, пробелы заменяются на дефис, спецсимволы удаляются. Например, заголовок «Привет, мир!» превращается в privet-mir. Подробнее о том, как делать красивые URL для SEO, читайте в нашей статье о генерации slug.
Когда без кириллицы в адресе не обойтись, браузер автоматически кодирует её при отправке запроса — вы можете видеть в адресной строке читаемый URL, но на сервер приходит уже закодированный вариант. Это называется IRI (Internationalized Resource Identifier), и стандарт IDNA описывает, как доменные имени с не-ASCII символами преобразуются в punycode.
URL кодирование в разных языках
JavaScript
// Кодирование значения параметра
const encoded = encodeURIComponent('тест & пример');
// Декодирование
const decoded = decodeURIComponent('%D1%82%D0%B5%D1%81%D1%82');
// Парсинг готового URL
const url = new URL('https://example.com/search?q=привет&lang=ru');
url.searchParams.get('q'); // "привет"
url.searchParams.set('q', 'новый запрос');
url.toString(); // готовый URL с корректным кодированиемPython
from urllib.parse import quote, unquote, urlencode
# Кодирование строки
encoded = quote('тест & пример') # "%D1%82%D0%B5%D1%81%D1%82%20%26%20%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80"
# Декодирование
decoded = unquote(encoded)
# Сборка query-строки
query = urlencode({'q': 'привет', 'lang': 'ru'})
# "q=%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82&lang=ru"PHP
<?php
$encoded = rawurlencode('тест & пример');
$decoded = rawurldecode($encoded);
// Для query-строк лучше использовать http_build_query:
$query = http_build_query(['q' => 'привет', 'lang' => 'ru']);URL encoding и безопасность
Правильное URL кодирование — важная часть защиты от атак. Рассмотрим два классических примера.
Open Redirect
Если ваш сайт делает редирект на URL из параметра, обязательно проверяйте, что получатель находится внутри вашего домена. Просто закодированный внешний URL легко пропустить через чёрный список, потому что атакующий может закодировать его дважды или использовать хитрые комбинации символов.
HTTP Parameter Pollution
Когда один и тот же параметр передаётся несколько раз, разные фреймворки ведут себя по-разному: одни берут первое значение, другие — последнее, третьи собирают массив. Атакующий может этим воспользоваться, добавив второй параметр с тем же именем.
Никогда не доверяйте данным из URL без валидации — даже если они закодированы. Percent-encoding сохраняет исходное значение, а не фильтрует опасные символы.
Когда percent-encoding не нужен
Многие разработчики злоупотребляют кодированием, превращая простые URL в нечитаемую кашу. Вот несколько случаев, когда кодировать не нужно:
- Буквы латиницы и цифры — они уже безопасны.
- Дефис, точка, подчёркивание, тильда — они в списке unreserved.
- Символы-разделители, если они действительно выполняют свою синтаксическую роль.
- Несколько слэшей подряд в пути (хотя это может запутать некоторые серверы).
В то же время кириллицу, пробелы, кавычки и угловые скобки кодировать нужно всегда. Хороший способ проверить себя — открыть URL в браузере: если в адресной строке он выглядит «как есть» и страница загружается корректно, всё в порядке.
Заключение
URL кодирование — простой, но критически важный механизм, без которого невозможна корректная работа с адресами, содержащими не-ASCII символы или спецзнаки. Главное — помнить о разнице между encodeURI и encodeURIComponent, кодировать значения параметров, а не весь URL целиком, и никогда не доверять данным из адресной строки без проверки.
Для практической работы у нас есть удобные инструменты: URL кодировщик и URL декодер. А если хотите понять, как URL соотносится с другими методами кодирования — читайте наш сравнительный обзор.
Попробуйте эти инструменты
Похожие статьи
Base64 — что это и как работает
Принцип кодирования Base64, алфавит, padding, использование в Data URI, email, API. Примеры кодирования.
HTML сущности и кодирование спецсимволов
HTML entities, named vs numeric, XSS защита, кодирование кавычек, амперсандов, угловых скобок.
JWT токен: структура и как декодировать
JSON Web Token: header, payload, signature. Как работает аутентификация JWT, безопасность, декодирование.
UTF-8 и BOM: что это такое и чем отличается от UTF-16
UTF-8 — что это такое простыми словами: как кодируется кириллица, зачем нужен BOM, чем UTF-8 отличается от UTF-16 и откуда берутся кракозябры.