flashman
← All posts

URL-encoded form bugs: plus signs, spaces, and webhooks

Debug URL-encoded form bugs by separating plus signs, spaces, percent escapes, charsets, and double encoding before changing OAuth or webhook code safely.

2026-07-04 · 5 min read · Rahul Chitturi

  • url-encoding
  • oauth
  • webhooks

application/x-www-form-urlencoded looks simple until a plus sign is a literal plus in one system and a space in another. OAuth redirects, payment webhooks, and legacy APIs all expose this edge case when clients mix query-string encoding with form-body decoding.

The safest fix starts by identifying which layer encoded the value and which layer decoded it.

Separate query strings from form bodies

Percent encoding is common to both URLs and form submissions, but form bodies historically decode plus signs as spaces. A value that survives in a query parameter may change when the same string is sent as a form field. Treat those contexts as separate formats during debugging.

  • Spaces may appear as %20 or + depending on the encoder
  • Literal plus signs should be percent-encoded as %2B when needed
  • Decode each layer once, then compare the intermediate value
  • Check whether the server library decodes form bodies automatically

Double encoding hides in redirects

OAuth state, return URLs, and webhook callback parameters often contain URLs inside URLs. If a redirect_uri is encoded twice, a provider may compare the wrong string. If it is decoded too early, reserved characters can break the surrounding request.

Log the value before encoding, after encoding, and after provider callback handling so the failing layer is visible.

Do not ignore charset drift

Most modern systems expect UTF-8, but older integrations can still assume a different charset. Non-ASCII names, currencies, and symbols reveal this quickly. Confirm the charset in headers and documentation before blaming signature verification or webhook replay logic.

A Flashman workflow

Use the URL encoder/decoder to inspect each layer, diff the pre-redirect and post-callback values, and format any JSON payload that carries the same field. If HTML entities appear in copied admin-console examples, decode them separately before comparing values.

Keep webhook secrets and OAuth client secrets out of examples. Reproduce with placeholder values that preserve spaces, plus signs, and percent escapes.

Try these tools