Document Workflow

Diagnose and recover one double-encoded query value

Decode one proven query-value layer at a time, distinguish `%25` from intentional escapes, and rebuild the outer URL without activating protected delimiters.

Written and tested by Published: Reviewed:

How this workflow was checked

We replayed “Reveal one remaining encoded path layer” in URL Encoder and Decoder, keeping the guide's input unchanged. “Identify the owning query value” and “Assign the remaining layer” defined the normal path; “Decoding the full URL in a loop” and “Changing signed data before verification” defined the boundary review before the output was accepted.

One decode pass changed %252Fdocs%2520v1%252Fapi%253Fpage%253D2 to the still-encoded %2Fdocs%20v1%2Fapi%3Fpage%3D2 layer, making the required stopping point explicit.

Problem

Double encoding is a layer problem, not a command to decode an entire URL repeatedly. `%252F` becomes `%2F` after one pass and `/` only after another. The first result may be exactly what the outer protocol needs, while a second pass can activate `/`, `?`, `&`, `=`, or `#` and change routing or parameter boundaries. Signed callbacks and OAuth state can also fail if any layer changes before signature verification. Recovery requires preserving the raw evidence, isolating one value, and proving the owner of each layer.

Sources and standards

These authoritative references define the formats or security boundaries used in this workflow. Tool-specific verification is documented separately above.

When to use this

  • A query value contains `%25` followed by two hexadecimal digits, such as `%252F`, `%2520`, or `%253A`.
  • A callback or webhook receives literal `%2F` text where the application expected a slash or path.
  • A proxy, SDK, CDN, form library, or template encoded a value that was already percent-encoded.
  • An OAuth, SSO, or signed redirect fails after a value crosses several transport layers.
  • You need a controlled one-pass comparison before changing production configuration.

Steps

  1. Step 1

    Capture the exact raw evidence

    Copy the complete URL or payload from the earliest available log without trimming, browser normalization, or repeated decode attempts. Redact secrets only in a separate working copy.

  2. Step 2

    Identify the owning query value

    Parse the outer URL and locate the exact key carrying `%25` sequences. Keep neighboring keys, repeated keys, and fragments separate so their delimiters are not decoded as data.

  3. Step 3

    Check syntax before decoding

    Confirm every `%` has two hexadecimal digits. A malformed escape and valid `%HH` triplets that form invalid UTF-8 are different failures; do not repair either silently.

  4. Step 4

    Decode the isolated value once

    Use RFC 3986 Component decode for the one value. The tool stops after one pass and warns if valid escapes remain instead of automatically mutating another layer.

  5. Step 5

    Assign the remaining layer

    Compare the output with the expected raw value and protocol documentation. Decode again only if the remaining `%HH` belongs to the same isolated value and the receiver expects decoded delimiters at that stage.

  6. Step 6

    Fix the producer and retest

    Remove the extra encode call at the owning application, SDK, proxy, or template. Rebuild the outer URL once, then test signatures, allowlists, routing, and the receiver's exact value in an authorized environment.

Example

Reveal one remaining encoded path layer

Input

%252Fdocs%2520v1%252Fapi%253Fpage%253D2

Output

%2Fdocs%20v1%2Fapi%3Fpage%3D2

Common mistakes

Decoding the full URL in a loop

A loop can activate outer separators and destroy the evidence needed to locate the responsible layer. Isolate one value and run one pass.

Assuming every remaining escape is an error

An escaped slash or question mark may be intentionally protected for a later transport boundary. Match each layer to its owner before changing it.

Changing signed data before verification

OAuth state, webhook signatures, and signed URLs can depend on exact bytes. Preserve the original and verify at the layer defined by the protocol.

Converting plus signs globally

A raw plus means space only under form-value rules. In RFC 3986 component data it can be a literal plus, so global replacement can corrupt the value.

Patching the observed URL instead of the producer

Manual cleanup treats one symptom. Remove the duplicate encode operation from the application, SDK, proxy, or template that owns the extra layer.

FAQ

Does `%25` always prove double encoding?

No. `%25` is the correct encoding of a literal percent sign. It suggests another layer when decoding once reveals `%HH` text that the next layer was expected to interpret.

How many times should I decode?

Once per proven layer and only on the isolated value. Stop after each pass, compare with the expected value, and identify who owns the remaining escapes.

Why does Full URL decode leave some `%HH` text?

Full URL decoding can preserve escapes for reserved delimiters so URL structure does not change. Component decode is appropriate after you have isolated one query value.

Can this fix an OAuth callback signature?

It can expose an encoding-layer mismatch, but it cannot verify or regenerate a signature. Preserve exact signed bytes and follow the provider's canonicalization rules.

What if the decoder reports invalid UTF-8?

The `%HH` syntax may be complete while the bytes are not valid UTF-8. Confirm the documented character encoding and do not replace bytes silently.

Where should the permanent fix go?

At the layer that encoded an already encoded value: commonly an application helper, SDK, proxy rewrite, form serializer, or template. Keep one encoding operation per required transport boundary.