Language : en | de | fr | es
Back to blogs
next post ▶
%20 in URL: What It Means and Why It Appears in Web Addresses

Double encoding in URLs

Double encoding in URLs happens when already encoded characters are encoded one more time. Instead of %20 for a space, you may end up with %2520, because the percent sign itself was encoded into %25. This issue looks small in logs, but it breaks redirects, API parameters, file paths, signatures, and security filters.

The topic matters because URL encoding is usually handled at several layers at once: browser, client library, proxy, framework, router, and application code. If two layers both decide to “help,” the final URL changes meaning. In production, this often appears as a broken callback URL, a failed OAuth flow, a missing file, or a route that works in staging but not behind a gateway.

Why double-encoded URLs appear in real systems

A URL should be encoded at the right stage and only for the relevant part. Problems start when developers encode the full string manually and then pass it into a tool that encodes it again.

Common causes include:

  • encoding a query parameter before giving it to URLSearchParams
  • encoding a full redirect URL and then embedding it inside another URL
  • framework middleware rewriting already processed values
  • frontend code sending encoded input to a backend that encodes it again
  • proxy or WAF rules transforming request components unexpectedly

A simple visual example

Suppose the original value is:

/docs/My File.pdf

Correct single encoding for a path fragment:

/docs/My%20File.pdf

After a second pass, it becomes:

/docs/My%2520File.pdf

The server may now search for a file literally containing %20 in its name rather than a space.

What the double URL encoding problem looks like

The hardest part is that the URL still looks “encoded,” so teams debug the wrong layer first. Symptoms depend on context.

Typical production symptoms

Symptom

What you see

Likely cause

Broken redirect

User lands on error page or wrong route

Redirect target encoded twice

Invalid API query

Backend receives mangled filter value

Client and server both encode

Signature mismatch

HMAC or signed URL check fails

Encoding changed after signing

Missing file or asset

Path resolves incorrectly

Encoded path segment re-encoded

Security rule bypass or false positive

Filter behaves inconsistently

Different decoding stages across layers

Why nested URLs are risky

A classic trap appears with return URLs:

https://app.example.com/login?next=https://site.example.com/account?tab=settings

If the inner URL must become a parameter value, it should be encoded correctly once. But many apps first encode it by hand, then a router or HTTP client encodes it again. That creates malformed callback flows and hard-to-read logs.

Double encode URL mistakes developers make

The most common mistake is not understanding which API expects raw data and which expects encoded data. Good libraries usually want unencoded values and handle escaping internally.

Example in JavaScript

Incorrect:

const next = encodeURIComponent("https://site.example.com/account?tab=settings");

const url = "/login?next=" + encodeURIComponent(next);

This produces a twice-transformed value.

Better:

const next = "https://site.example.com/account?tab=settings";

const url = "/login?next=" + encodeURIComponent(next);

Example with query builders

Incorrect pattern:

  • manually encode name=John Doe
  • pass the result into a query builder
  • query builder encodes % again

Correct pattern:

  • pass raw John Doe
  • let one trusted layer build the final query string

How to decode double encoding URLs safely

Decoding is easy only when you know how many times the value was transformed. Blind repeated decoding is dangerous, especially in security-sensitive code, because it can alter intended literals or help payloads bypass validation.

Practical decoding approach

  1. Inspect the raw request value from logs or a debugging proxy.
  2. Decode once and compare the result.
  3. If percent sequences remain where plain characters should be, inspect whether a second decode is justified.
  4. Normalize only at one controlled boundary.
  5. Validate after normalization, not before.

Example:

Original: hello%2520world

Decode once: hello%20world

Decode twice: hello world

That shows a value encoded two times. But do not turn this into a blind “decode twice” rule for every request.

Safer debugging checklist

  • capture the exact raw URL
  • separate path, query, and fragment
  • test one decode step at a time
  • verify which layer performed each transformation
  • confirm whether the value was supposed to stay encoded

Prevent double URL encoding by design

The best fix is architectural discipline, not patching.

Rules that work in practice

  • encode at the boundary where the final URL is assembled
  • keep internal values raw as long as possible
  • never manually encode before passing data to a trusted URL builder
  • do not decode and re-encode without a specific reason
  • document whether helpers expect raw or encoded input
  • sign URLs only after the final canonical form is ready

Good team convention

Pick one place responsible for URL transformation:

  • frontend router
  • backend URL helper
  • API client library
  • gateway normalization layer

Once responsibility is clear, duplicate escaping drops sharply.

Double URL encode edge cases in nested parameters

Some cases look like bugs but are actually intentional. For example, if one URL is embedded inside another as data, encoding must preserve separators such as ?, =, and & inside the nested value. The key is that the nested URL should be encoded once for its role as a parameter value, not repeatedly at each hop.

Example: redirect parameter

/auth?return_to=https%3A%2F%2Fapp.example.com%2Fdashboard%3Fpage%3D2

This is normal. It becomes a problem only if the same value later turns into:

/auth?return_to=https%253A%252F%252Fapp.example.com%252Fdashboard%253Fpage%253D2

That second version usually indicates extra processing.

Handling already transformed values in APIs and middleware

Framework stacks often mix automatic and manual handling. A reverse proxy may normalize once, the framework may decode into route params, and custom middleware may apply another pass. The result is inconsistency between logs, handler input, and downstream service calls.

Where to audit first

  • reverse proxy rewrite rules
  • CDN or WAF normalization settings
  • route parameter extraction
  • redirect helpers
  • third-party SDK request builders
  • custom security filters

A short audit across these layers usually reveals why a value changed more than expected.

Final takeaway

Double encoding is rarely a low-level mystery. It is usually a coordination failure between layers that all try to help with the same URL. The fix is to treat encoding as a single-responsibility step, keep values raw internally, and normalize carefully at the edge.

When a URL looks wrong, do not just patch it with extra decoding. Trace the full path of the value: where it started, who encoded it, who encoded it again, and whether the final component was a path, query value, or nested URL. That approach solves the bug without creating a more fragile system.