Try the URL Encoder/Decoder

A URL Inside Another URL Needs Different Encoding Than a Standalone URL — OAuth redirect_uri, Double Encoding, and RFC 3986

OAuth's redirect_uri must be percent-encoded when it appears as a query parameter value — because its own URL characters (://, /, ?) would otherwise be interpreted as URL structure rather than literal data. Here's RFC 3986's three character categories (unreserved never encoded, reserved encoded when used as data), why `+` means space in form encoding but not URI encoding, the double encoding signature (%25 in a URL), and the difference between JavaScript's encodeURI() and encodeURIComponent().

August 4, 2026 6 min read
Share: Facebook WhatsApp LinkedIn Email
A URL Inside Another URL Needs Different Encoding Than a Standalone URL — OAuth redirect_uri, Double Encoding, and RFC 3986

OAuth 2.0's redirect_uri parameter has stricter encoding requirements than almost any other URL component — because it appears inside another URL, inside an HTML page, potentially inside a JavaScript string, potentially inside a shell command — and each layer applies its own encoding rules, creating a sequence of encoding and decoding operations where a single mismatch produces an error that's extremely difficult to diagnose from the error message alone

URL encoding (percent-encoding) is one of those topics that seems trivially simple until you encounter a real-world bug. The percent sign followed by two hex digits looks obvious in isolation. In production, encoding bugs hide inside configuration strings, appear in log files that have been double-encoded, corrupt data passed between systems that encode differently, and produce error messages that don't mention encoding at all.


The percent-encoding specification and its reserved vs unreserved categories

RFC 3986 (the authoritative URI specification) divides characters into three categories:

Unreserved characters: A-Z, a-z, 0-9, -, _, ., ~ — these must NOT be percent-encoded. Encoding them is technically valid (the spec permits encoding any character) but is unnecessary and can cause comparison failures if one system encodes A as %41 and another leaves it as A.

Reserved characters: :, /, ?, #, [, ], @, !, $, &, ', (, ), *, +, ,, ;, = — these have syntactic significance in URIs. They should be encoded when they appear as literal data values, not as URI structure characters.

Other characters: all characters not in the above categories, including spaces (encoded as %20), most Unicode characters (encoded as UTF-8 bytes, each byte separately percent-encoded), and control characters.

The common confusion: + for space encoding. HTML form encoding (application/x-www-form-urlencoded) uses + to represent spaces. URI encoding (RFC 3986) uses %20. When URL-encoded form data is used in a URI context, + signs may not be decoded to spaces, creating bugs where form submissions containing spaces produce + signs in the URL that appear literally instead of as spaces.


When URL encoding applies in different contexts

The key principle: different parts of a URL have different encoding contexts, and the same character may need different treatment in each.

Path component: /api/resource/value — path segments are delimited by /. A / appearing as literal data within a path segment must be encoded as %2F. Other reserved characters may or may not need encoding depending on their meaning in context.

Query string: ?key=value&other=value2 — key-value pairs are delimited by & and =. These must be encoded if they appear within key or value strings. Spaces become %20 (URI encoding) or + (form encoding).

Fragment: #section-name — the fragment is never sent to the server; it's client-side only.

A URL within a URL (as a query parameter value): the most complex case:

https://auth.example.com/authorize?
  response_type=code
  &client_id=abc123
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback%3Fstate%3D123

The redirect_uri value must have its own URL-special characters (://, /, ?, =) percent-encoded, because they appear inside a query parameter value where they'd otherwise be interpreted as URL structure rather than literal data.


OAuth redirect_uri: the most common URL encoding bug in production

The OAuth 2.0 flow requires the redirect_uri parameter in the authorization request to exactly match the registered redirect URI for the client. "Exactly match" means character-for-character identical after decoding — or in strict implementations, before decoding:

The common failure scenario:

  1. Developer registers redirect URI: https://app.example.com/callback
  2. Application constructs the authorization URL using a URL builder that encodes the redirect_uri: redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
  3. Authorization server receives the request, decodes redirect_uri to https://app.example.com/callback → matches registered URI → OK
  4. Developer adds a state parameter: now redirect URI becomes https://app.example.com/callback?state=xyz
  5. Encoded: redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback%3Fstate%3Dxyz
  6. But the registered URI is https://app.example.com/callback — no state parameter
  7. After decoding, the redirect_uri has ?state=xyz appended → doesn't match registered URI → OAuth error: redirect_uri mismatch

The correct approach: the state parameter should be a top-level OAuth parameter, not appended to the redirect_uri. The redirect_uri should remain constant and registered.


Double encoding: when encoded content gets encoded again

Double encoding occurs when percent-encoded content is encoded a second time:

Original: Hello World First encoding: Hello%20World Second encoding (treating % as a character to encode): Hello%2520World

The %25 signature: %25 in a URL is the percent-encoded percent sign. Seeing %25 in a URL often indicates double encoding — the original content had a %20 (or other percent sequence) that was then encoded again, turning % into %25.

How double encoding happens in practice:

  • A URL is stored in a database already encoded
  • Code retrieves it and encodes it again before use
  • Template engines that encode on output when the input is already encoded
  • Copying a URL from an already-encoded display (browser address bar) and pasting it into a system that encodes it again

Punycode and international domain names in URLs

International domain names (IDN) — domains containing non-ASCII characters — are represented in DNS as Punycode, which uses only ASCII characters:

münchen.dexn--mnchen-3ya.de (Punycode representation)

The URL context: a URL containing an IDN domain has the Punycode version in the actual URL used for connections, while browsers display the Unicode version in the address bar:

  • What the browser displays: https://münchen.de/page
  • What's actually in the HTTP request: https://xn--mnchen-3ya.de/page

The homograph attack: visual similarity between Unicode characters from different scripts enables "lookalike" domains. аpple.com (with a Cyrillic 'а') looks identical to apple.com (with a Latin 'a') but is a completely different domain. Browsers now display the Punycode for domains using mixed scripts to prevent this — xn--pple-43d.com is unambiguous.


How to use the URL Encoder/Decoder on sadiqbd.com

  1. For OAuth debugging: decode the complete authorization URL from your logs or browser address bar to see the human-readable version of each parameter, identifying any encoding issues in the redirect_uri or state parameter
  2. For double encoding detection: if a URL behaves unexpectedly, decode it twice — if the second decode produces a different result from the first, the URL was double-encoded
  3. For query parameter encoding validation: encode and decode your API query parameter values to verify that special characters (spaces, &, =, +) in values are correctly escaped, particularly when values themselves contain URL-reserved characters

Frequently Asked Questions

What's the difference between encodeURI() and encodeURIComponent() in JavaScript, and when should I use each? encodeURI() encodes a complete URL, leaving URL-structure characters intact; encodeURIComponent() encodes a string for use as a URL component value, encoding all characters that have special meaning in URL structure. encodeURI('https://example.com/path?key=value with spaces')https://example.com/path?key=value%20with%20spaces — the ://, /, ?, and = are left unencoded because they're valid URL structure characters. encodeURIComponent('value with spaces & special=chars')value%20with%20spaces%20%26%20special%3Dchars — the & and = are encoded because they'd be interpreted as query string delimiters if left raw in a parameter value. Use encodeURI for complete URLs; use encodeURIComponent for individual parameter values that you then assemble into a URL.

Is the URL Encoder/Decoder free? Yes — completely free, no sign-up required.

Try the URL Encoder/Decoder free at sadiqbd.com — percent-encode and decode URLs and URL components instantly.

Share: Facebook WhatsApp LinkedIn Email

URL Encoder/Decoder

Free, instant results — no sign-up required.

Open URL Encoder/Decoder →
Similar Tools
HTML Entities Regex Tester JSON Formatter Color Converter Random String Generator JSON Unescape & Cleaner Base64 Encoder/Decoder Number Base Converter
URL Structure: Query String Parsing Ambiguities, Punycode, and Open Redirect Vulnerabilities
Developer
URL Structure: Query String Parsing Ambiguities, Punycode, and Open Redirect Vulnerabilities
URL Design as API Design: REST Conventions, Versioning Strategies, and the Long-Term Cost of Changing URLs
Developer
URL Design as API Design: REST Conventions, Versioning Strategies, and the Long-Term Cost of Changing URLs
URL Encoding Context: Why a URL Inside Another URL, in HTML, and in a Shell Command All Need Different Treatment
Developer
URL Encoding Context: Why a URL Inside Another URL, in HTML, and in a Shell Command All Need Different Treatment
Why OAuth redirect_uri Errors Are Almost Always Encoding Problems — and How to Fix Them
Developer
Why OAuth redirect_uri Errors Are Almost Always Encoding Problems — and How to Fix Them