Профили используют одинаковые UTF-8 escape, но по-разному записывают некоторые значения. Компонент RFC 3986 оставляет буквы, цифры, дефис, точку, подчёркивание и тильду. Компонент HTML-формы также оставляет звёздочку, но пробел пишет специальным плюсом.

%20 и + зависят от профиля

Компонент RFC записывает пробел как %20, а буквальный плюс как %2B. Форма записывает пробел как +, но буквальный плюс тоже как %2B. Поэтому декодеру нужен профиль: без него нельзя понять, означает сырой плюс плюс или пробел. URLCodec не угадывает.

Компонент — не полный URL

Разделители /, ?, &, = и # формируют полный URL, но внутри одного значения являются данными и получают escape. У URLCodec намеренно нет режима полного URL: пользователь сначала определяет границу и не получает красивый, но неоднозначный результат со смешением синтаксиса и данных.

Проверьте целевой компонент

Процентное кодирование зависит от контекста. URLCodec намеренно преобразует одно значение и не разбирает полный URL, не делит query-пары, не нормализует путь, не проверяет адрес и не решает, ждёт ли другая система правила HTML-формы. Вставляйте результат только в тот компонент, контракт которого проверен. Кодирование меняет представление, но не шифрует, не аутентифицирует, не очищает и не делает адрес безопасным.