← Back to Blog

URL Encoding Failures: Broken Redirects, OAuth Callbacks, and Query Strings

Utility · June 5, 2026 · 5 min read

A callback URL with &, =, or spaces breaks an OAuth flow or a redirect. URL encoding issues are subtle. Fix them with methodical decoding tools.

Technical cover image for URL Encoding Failures: Broken Redirects, OAuth Callbacks, and Query Strings

The OAuth Callback Returns 400 and You Cannot See Why

Your OAuth redirect URL is https://example.com/auth/callback?state=abc&code=xyz. But the provider sends you to https://example.com/auth/callback?state=abc%26code%3Dxyz — and your handler expects the literal query parameters, not a single encoded one. URL encoding issues are invisible in browser address bars because browsers decode them for display.

Use OpsCheck URL Encoder/Decoder to see what the URL really contains, not what the browser renders. Pair with OpsCheck HTTP Headers to verify the actual Location header value in redirect responses.

Diagnosing Encoding Failures

# Decode a suspicious URL to see its real structure
python3 - <<'PY'
from urllib.parse import unquote, urlparse
url = "https://example.com/auth/callback?state=abc%26code%3Dxyz"
parsed = urlparse(url)
print("Raw query:", parsed.query)
print("Decoded:", unquote(parsed.query))
PY

Double-Encoding: The Most Common Trap

# Original URL: https://example.com/callback?redirect=https://app.example.com/dashboard
# Single-encoded (correct): https://example.com/callback?redirect=https%3A%2F%2Fapp.example.com%2Fdashboard
# Double-encoded (broken): https://example.com/callback?redirect=https%253A%252F%252Fapp.example.com%252Fdashboard

# Detect double-encoding:
python3 - <<'PY'
from urllib.parse import unquote
encoded = "https%253A%252F%252Fapp.example.com%252Fdashboard"
once = unquote(encoded)
print("Once:", once)  # https%3A%2F%2Fapp.example.com%2Fdashboard — still encoded!
twice = unquote(once)
print("Twice:", twice)  # https://app.example.com/dashboard — correct at last
PY

Real-World Scenario

A SAML authentication flow broke after a reverse proxy upgrade. The IdP sent a SAMLResponse parameter containing a Base64-encoded XML blob with '+' characters. The old proxy passed it through; the new proxy encoded '+' as '%2B' in the redirect URL. The SP received the SAMLResponse with %2B instead of +, Base64 decoding produced garbled XML, and authentication failed with a cryptic "invalid SAML response" error.

The fix was adding proxy_set_header Accept-Encoding ""; to prevent the proxy from re-encoding the response body. The OpsCheck JSON Formatter helped inspect the decoded SAML assertion, and the OpsCheck URL Encoder/Decoder confirmed the + vs %2B difference.

# Verify a URL parameter character by character
python3 - <<'PY'
from urllib.parse import quote, unquote
test = "abc+def=xyz"
print("Encoded:", quote(test, safe=""))
print("Decoded:", unquote(quote(test, safe="")))
# + becomes %2B when fully encoded, but + itself decodes to space in query strings!
PY

Debugging Checklist

  • Log the raw URL, not the decoded version — encoding is invisible otherwise
  • Check for double-encoding: decode once and verify the result looks normal
  • Test with special characters: &, =, +, %, space, unicode characters
  • Verify reverse proxy behavior: some proxies re-encode URLs
  • For OAuth/SAML, capture the exact redirect Location header value
  • Use OpsCheck URL Encoder/Decoder to simulate encoding scenarios