Debugging

How to debug a 404 from bad URL parameters.

When a URL returns 404 but the page clearly exists, encoding is often the culprit: an encoded slash the server rejects, a double-encoded value, an unencoded # that truncates the path, or a stray space. Here’s a systematic way to find which one.

Step 1: decode the URL and read it

Before anything else, decode the failing URL so you can see what the server actually received. Decode recursively in case it’s double-encoded:

Failing:  /search?q=hello%2520world
Decoded once:  /search?q=hello%20world
Decoded twice: /search?q=hello world   ← it was DOUBLE-encoded

If the value only reads correctly after two passes, something encoded an already-encoded string. Fix the code so it encodes exactly once, at the point the URL is built.

Step 2: check the usual encoding culprits

SymptomCauseFix
404 on a path with %2FServer rejects encoded slashes in the path by defaultMove the value to the query string, or enable encoded-slash handling
%2520 in the URLDouble-encoding (% got encoded to %25)Encode once only
Everything after # is missing server-sideUnencoded # starts the fragment (never sent to server)Encode a literal # in data as %23
400/404 with a raw spaceSpace isn’t legal in a URLEncode as %20 (path) or + (query)
Two “different” URLs, one 404sMixed hex casing or param orderNormalize casing; check routing is case-consistent

Step 3: compare what was sent vs what the route expects

Parse the failing URL into its parts and confirm the path is what your router matches on. A common surprise: an encoded slash that the framework decoded, turning one route segment into two.

const u = new URL(failingUrl);
u.pathname;               // what the router sees
u.searchParams.toString(); // the parameters, decoded
// Compare pathname against your actual route pattern

Step 4: check the # and & didn’t split your value

If a parameter value legitimately contains & or # and wasn’t encoded, everything after it leaks out of the value — the server sees a different (often nonexistent) route or a truncated parameter. Encode & as %26 and # as %23 inside values. Building values with URLSearchParams does this for you.

A quick workflow

Paste the failing URL into the URL parser to see its parts, and the decoder (recursive mode) to catch double-encoding. Our troubleshooting guide lists ten encoding bugs and their fixes.

About this page

Written and maintained by the urlencodedecode.com team. Every technical claim on this page is verified against primary sources — the RFCs (3986, 3629, 4648, 7578), the WHATWG URL Standard, and official vendor or language documentation — rather than second-hand summaries. When a source contradicts a common assumption, we follow the source and note the discrepancy. Corrections: contactus@urlencodedecode.com.

Common questions

Frequently asked.

Encoding is a common cause: the server may reject an encoded slash (%2F) in the path, the value may be double-encoded (%2520), an unencoded # may have truncated the path, or a raw space made the URL invalid. Decode the failing URL to see what the server actually received.

Many servers and proxies reject a percent-encoded slash in the path by default, as a defence against path-traversal tricks, or decode it early and split one route segment into two. Move the value into the query string, or enable your server's encoded-slash handling if you truly need %2F in the path.

It's a double-encoded space: a space was encoded to %20, then that string was encoded again, turning the % into %25 to give %2520. Decode twice to confirm, then fix the code so it encodes the value exactly once at the point the URL is built.

An unencoded # starts the fragment, which the browser never sends to the server, so everything after it disappears server-side. An unencoded & inside a value ends that parameter early. Encode # as %23 and & as %26 when they're data — URLSearchParams does this automatically.

Decode the failing URL (recursively, to catch double-encoding), then parse it into path and query and compare the path against your actual route pattern. Check for encoded slashes, an unencoded # or & splitting a value, raw spaces, and mixed hex casing.

Related

Keep exploring.