Веб

URL кодирование и декодирование: плюс в URL

Как правильно выполнять URL кодирование и декодирование, чтобы избежать проблем с символом плюс (+) в GET-параметрах. Подробное руководство.

Как правильно выполнить URL-кодирование и декодирование специальных символов, таких как знак плюс (+), в параметрах GET-запроса? Например, как избежать преобразования + в пробел при передаче email-адресов в строке запроса ?email=email+chtoto@gmail.com и корректно обработать такие данные?

Чтобы корректно передать символ + в GET-параметрах, используйте правильное url кодирование и url декодирование: кодируйте значения параметров (например email) через encodeURIComponent или средства уровня библиотек, которые превратят + в %2B. Если оставить + в строке запроса как ?email=email+chtoto@gmail.com, парсер для application/x-www-form-urlencoded может интерпретировать его как пробел — поэтому безопасный вид URL должен быть ?email=email%2Bchtoto%40gmail.com. На сервере декодируйте параметры стандартными средствами (например decodeURIComponent или встроенные парсеры), чтобы получить оригинальный email+chtoto@gmail.com.

Содержание

Как работает URL кодирование и декодирование

URL (URI) использует percent-encoding: некоторые символы заменяются на %HH (hex). Существует разница между кодированием всей строки URL и кодированием отдельной части (компонента) — для значений параметров всегда используйте кодирование компонент. В JavaScript это encodeURIComponent (кодирует почти все специальные символы), а не encodeURI (которая пропускает ряд символов, допустимых в целом URL).

  • Общая спецификация синтаксиса URI приведена в RFC 3986; правила кодирования задают, какие символы должны быть экранированы.
  • Для form/query-параметров действует алгоритм application/x-www-form-urlencoded: пробелы обычно кодируются как +, а остальные недопустимые символы — через %HH (подробности в спецификации HTML).

Подробнее о функциях кодирования/декодирования и их поведении можно посмотреть на страницах документации и спецификации (см. ссылки в разделе Источники).

Ключевые понятия

  • url кодирование — перевод символов в безопасные для URL последовательности (%2B для +, %40 для @ и т.д.).
  • url декодирование — обратная операция (например decodeURIComponent в JS).
  • Для значений параметров используйте кодирование компонент, а не всей строки URL.

Кодирование плюса в URL и проблема «плюс -> пробел»

Почему ?email=email+chtoto@gmail.com превращает + в пробел? Потому что при разборе query-параметров в контексте application/x-www-form-urlencoded символ + интерпретируется как пробел. Если вы вручную собрали URL и забыли закодировать + в %2B, серверный парсер декодирует + как пробел, и вы получите email chtoto@gmail.com вместо email+chtoto@gmail.com.

Спецификация HTML описывает алгоритм application/x-www-form-urlencoded, поэтому поведение парсеров соответствует стандарту — плюс используется как эквивалент пробела в таком кодировании. Для избежания этой проблемы нужно явно кодировать значения параметров, в частности все + заменять на %2B.

Полезная справка по алгоритму кодирования форм: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#application/x-www-form-urlencoded-encoding-algorithm

Как правильно кодировать значения параметров (клиент)

Общее правило: никогда не формируйте строку запроса простым конкатенированием “ключ=значение” без кодирования значения. Для кодирования плюса в URL и других специальных символов используйте готовые функции.

JavaScript (браузер/Node):

  • encodeURIComponent(value) — кодирует + как %2B, @ как %40 и т.д.
  • URLSearchParams — удобный интерфейс для сборки query-строк и автоматического кодирования.

Примеры:

javascript
// encodeURIComponent
const email = 'email+chtoto@gmail.com';
const url = '/api?email=' + encodeURIComponent(email);
// url = "/api?email=email%2Bchtoto%40gmail.com"

// URLSearchParams
const params = new URLSearchParams({ email: 'email+chtoto@gmail.com' });
const url2 = '/api?' + params.toString();
// url2 = "/api?email=email%2Bchtoto%40gmail.com"

MDN по encodeURIComponent и URLSearchParams: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent и https://developer.mozilla.org/ru/docs/Web/API/URLSearchParams

Curl (правильная отправка параметра с кодированием):

bash
curl -G --data-urlencode "email=email+chtoto@gmail.com" "https://example.com/subscribe"

--data-urlencode гарантирует, что + превратится в %2B.

Если вы используете HTML-форму, браузер обычно сам применяет application/x-www-form-urlencoded и закодирует + как %2B (если пользователь ввёл +), но при ручном формировании URL из JavaScript — всегда кодируйте явно.

Как правильно декодировать на сервере и частые ошибки

Серверные фреймворки (Express, Flask, PHP и т.д.) обычно декодируют query-параметры согласно application/x-www-form-urlencoded. Если клиент корректно закодировал + как %2B, то после декодирования вы получите реальный символ +. Если же клиент отправил незакодированный +, парсер вернёт пробел.

Пример на Express (Node.js):

javascript
app.get('/subscribe', (req, res) => {
  // если клиент прислал ?email=email%2Btag%40gmail.com -> req.query.email === 'email+tag@gmail.com'
  // если клиент прислал ?email=email+tag%40gmail.com  -> req.query.email === 'email tag@gmail.com'
  const email = req.query.email;
});

Python (Flask/Werkzeug) и PHP ведут себя аналогично: парсеры заменяют + на пробел при декодировании application/x-www-form-urlencoded.

Если данные приходят с пробелом вместо плюса и вы уверены, что это «плюсовая» адресация (например user+tag@example.com), можно:

  • на стороне клиента исправить кодирование (лучше всего);
  • или на сервере анализировать request.url / raw query string: если в необработанной (сырой) части запроса вы видите незакодированные +, это подтверждение причины; при необходимости можно попытаться «восстановить» плюс в локальной части email (см. раздел отладки), но это небезопасная эвристика.

Примеры: JavaScript, Python, PHP

JavaScript (клиент):

javascript
const email = 'email+chtoto@gmail.com';
const safeUrl = '/api?email=' + encodeURIComponent(email);
// safeUrl -> '/api?email=email%2Bchtoto%40gmail.com'

Node.js (разбор сырой query и исправление, если нужно — НО с оговоркой):

javascript
const raw = req.url.split('?')[1] || '';
// Если хотим интерпретировать literal '+' как '+', а не как пробел:
const fixed = raw.replace(/(^|&)(email)=([^&]*)/, (m, p1, p2, val) => {
  // заменим все '+' на '%2B' в значении перед декодированием
  const safeVal = val.replace(/\+/g, '%2B');
  return `${p1}${p2}=${safeVal}`;
});
// Теперь можно безопасно decodeURIComponent нужного параметра

Осторожно: такой подход меняет поведение для случаев, где + означал пробел.

Python (конструирование query и парсер):

python
from urllib.parse import urlencode, parse_qs

params = {'email': 'email+chtoto@gmail.com'}
qs = urlencode(params)  # 'email=email%2Bchtoto%40gmail.com'

# Разбор:
parsed = parse_qs('email=email%2Bchtoto%40gmail.com')
# parsed['email'][0] == 'email+chtoto@gmail.com'

Документация по urllib.parse: https://docs.python.org/3/library/urllib.parse.html

PHP:

php
// Формирование:
$query = http_build_query(['email' => 'email+chtoto@gmail.com']);
// 'email=email%2Bchtoto%40gmail.com'

// Доступ:
$email = $_GET['email']; // если клиент корректно закодировал -> 'email+chtoto@gmail.com'

Отладка, тесты и исправление повреждённых данных

Как быстро проверить, где потерялся плюс:

  • Смотрите в браузерную адресную строку: если вы видите %2B — всё в порядке. Если видите + — вероятно сервер интерпретирует как пробел.
  • Логируйте «сырую» строку запроса (raw request line) на сервере, чтобы увидеть, было ли %2B или +.
  • Тестируйте с curl --data-urlencode и с ручной вставкой ?email=email%2Btag@example.com.

Если данные уже сохранены с пробелом вместо плюса и вы уверены, что это результат неправильного кодирования, можно попытаться восстановить локальную часть email (то, что до @):

python
def fix_email(s):
    if ' ' in s and '@' in s:
        local, sep, domain = s.partition('@')
        local = local.replace(' ', '+')
        return local + sep + domain
    return s

Эта эвристика применима только если вы уверены, что пробел появился вместо + — иначе можно исказить валидные данные.

Рекомендации — чеклист действий

  • Всегда кодируйте значения параметров: encodeURIComponent, URLSearchParams, urlencode/http_build_query.
  • Для передачи email в GET используйте: ?email=${encodeURIComponent(email)}.
  • Предпочитайте POST + JSON для передачи сложных или чувствительных данных (там нет ambiguité с +).
  • Логируйте raw query string при отладке.
  • Избегайте «ручной» сборки query строк через конкатенацию без кодирования.
  • Проверяйте отсутствие двойного кодирования (%252B = закодированный %2B).

Источники

Заключение

Корректное url кодирование и url декодирование — ключ к тому, чтобы символ + не превращался в пробел в GET-параметрах. Кодируйте значения параметров (например email) с помощью encodeURIComponent / URLSearchParams / urlencode перед отправкой; на сервере используйте стандартные парсеры для декодирования. Если столкнулись с уже «поломанными» данными, сначала проверьте raw query string и только затем применяйте осторожные эвристики восстановления.

Авторы
Проверено модерацией
Модерация
URL кодирование и декодирование: плюс в URL