Руководство8 мин чтения

Что такое процентное кодирование? (RFC 3986 простыми словами)

Процентное кодирование — это механизм, определённый в RFC 3986, для кодирования специальных символов в URI путём их замены на знак процента (%), за которым следуют две шестнадцатеричные цифры. Оно гарантирует, что URL-адреса содержат только допустимые ASCII-символы, которые можно безопасно передавать по интернету.

Что такое процентное кодирование?

Процентное кодирование, также называемое URL-кодированием, — это стандартный способ представления символов в URI (Uniform Resource Identifier, унифицированном идентификаторе ресурса), которые недопустимы или имеют специальное значение. Оно работает за счёт замены каждого символа, требующего кодирования, на знак процента (%), за которым следуют две шестнадцатеричные цифры, представляющие байтовое значение символа.

Например, символ пробела (байтовое значение 0x20) кодируется как %20. Знак «собака» @ (байтовое значение 0x40) кодируется как %40. Этот механизм гарантирует, что любые данные можно безопасно встроить в URI, не вступая в конфликт с синтаксисом URI.

Термин «процентное кодирование» происходит от использования символа процента в качестве экранирующего префикса. Формальная спецификация определена в RFC 3986, опубликованном Инженерным советом Интернета (IETF) в январе 2005 года; его авторы — Тим Бернерс-Ли, Рой Филдинг и Ларри Мазинтер.

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

Процесс кодирования состоит из следующих шагов: сначала символ преобразуется в его байтовое представление с использованием той или иной кодировки символов (на современном вебе практически всегда это UTF-8). Затем каждый байт представляется в виде знака процента, за которым следуют две шестнадцатеричные цифры (по соглашению используются заглавные буквы A-F, хотя декодеры должны принимать и строчные).

// Пошаговое кодирование символа пробела
// Символ: ' ' (пробел)
// Байтовое значение ASCII/UTF-8: 0x20 (десятичное 32)
// Процентное кодирование: %20

// Пошаговое кодирование многобайтового символа: e с акутом
// Символ: e с акутом (U+00E9)
// Байты UTF-8: 0xC3 0xA9
// Процентное кодирование: %C3%A9

// Пошаговое кодирование трёхбайтового символа
// Символ: иероглиф CJK (U+4E2D)
// Байты UTF-8: 0xE4 0xB8 0xAD
// Процентное кодирование: %E4%B8%AD

Шестнадцатеричные цифры всегда должны идти парами. Одиночный знак процента или знак процента, за которым следуют не шестнадцатеричные символы, недопустим и приведёт к ошибке декодирования. Это распространённая причина ошибки «URI malformed» в JavaScript.

Процентное кодирование всегда обратимо. Декодер считывает знак процента, берёт следующие два символа как шестнадцатеричное байтовое значение и преобразует его обратно в исходный символ. Для многобайтовых символов UTF-8 последовательно закодированные байты объединяются и декодируются вместе.

Какие символы должны кодироваться?

Не все символы требуют процентного кодирования. RFC 3986 определяет две категории: незарезервированные символы, которые никогда не нуждаются в кодировании, и зарезервированные символы, которые должны кодироваться, когда они не используются по своему зарезервированному назначению. Все остальные символы (включая пробелы, символы не из ASCII и управляющие символы) должны кодироваться всегда.

Символы, которые НЕ нуждаются в кодировании (незарезервированные): заглавные буквы (A-Z), строчные буквы (a-z), цифры (0-9), дефис (-), точка (.), символ подчёркивания (_) и тильда (~). Эти 66 символов могут появляться в любом месте URI без кодирования.

Символы, которые иногда нуждаются в кодировании (зарезервированные): : / ? # [ ] @ ! $ & ' ( ) * + , ; =. Эти символы имеют специальное синтаксическое значение в URI. Они нуждаются в кодировании только тогда, когда используются как данные, а не как разделители.

Символы, которые всегда нуждаются в кодировании: пробелы, символы не из ASCII (любой символ выше 127), управляющие символы (0–31 и 127) и небезопасные символы, такие как < > { } | \ ^ ` ".

Зарезервированные и незарезервированные символы в RFC 3986

Различие между зарезервированными и незарезервированными символами имеет основополагающее значение для того, как работают URI. Зарезервированные символы служат разделителями, которые определяют структуру URI. Например, :// отделяет схему от компонента authority, / разделяет сегменты пути, ? начинает строку запроса, а # начинает фрагмент.

Категория Символы Когда кодировать
Незарезервированные A-Z a-z 0-9 - . _ ~ Никогда
Общие разделители : / ? # [ ] @ Когда используются как данные, а не как разделители
Субразделители ! $ & ' ( ) * + , ; = Когда используются как данные в компонентах, придающих им значение
Все остальные символы Пробелы, символы не из ASCII, управляющие символы и т. д. Всегда

Ключевой принцип RFC 3986 заключается в том, что URI, различающиеся только тем, закодирован ли зарезервированный символ процентным кодированием или присутствует буквально, не являются эквивалентными. Например, /path/to и /path%2Fto — это разные URI, даже несмотря на то, что %2F декодируется в /. У первого два сегмента пути; у второго — один сегмент пути, содержащий буквальный слеш.

Процентное кодирование и URL-кодирование: это одно и то же?

«Процентное кодирование» и «URL-кодирование» часто используются как взаимозаменяемые термины, и в большинстве случаев они означают одно и то же. Тем не менее есть тонкое историческое различие. «URL-кодирование» иногда может относиться к более старому формату application/x-www-form-urlencoded, используемому HTML-формами, который кодирует пробелы как + вместо %20.

Формат application/x-www-form-urlencoded был определён в спецификации HTML и появился раньше RFC 3986. У него немного другие правила: пробелы становятся +, а набор символов, которые не кодируются, слегка отличается. Современное употребление термина «URL-кодирование» практически всегда относится к процентному кодированию по RFC 3986, где пробелы — это %20.

На практике вам следует использовать процентное кодирование по RFC 3986 для всех контекстов URI (сегментов пути, параметров запроса в REST API, идентификаторов фрагментов). Формат application/x-www-form-urlencoded используйте только при работе с отправкой HTML-форм или когда этого требует конкретный API.

// Процентное кодирование по RFC 3986 (рекомендуется для URI)
encodeURIComponent('hello world')  // "hello%20world"

// application/x-www-form-urlencoded (HTML-формы)
new URLSearchParams({q: 'hello world'}).toString()  // "q=hello+world"

// Эквивалент на Python
from urllib.parse import quote, quote_plus
quote('hello world', safe='')   # "hello%20world"
quote_plus('hello world')       # "hello+world"

Похожие статьи

Попробуйте наши бесплатные инструменты