The 403 Bypass Playbook: Path Normalization and Reverse-Proxy Discrepancies
A 403 is rarely the end of the story. Here's the playbook senior testers run when an endpoint says Forbidden. You find a juicy endpoint during recon: GET /admin/dashboard ➔ 403 Forbidden Most people close the tab and
A 403 is rarely the end of the story. Here's the playbook senior testers run when an endpoint says Forbidden.
You find a juicy endpoint during recon:
GET /admin/dashboard ➔ 403 Forbidden
Most people close the tab and move on. Senior testers treat a 403 as an invitation to test parser discrepancy — because modern apps aren't one monolith. They sit behind edge gateways, WAFs and reverse proxies (Cloudflare, Nginx, HAProxy, Envoy) that route to backends (Node, Spring Boot, Flask, Go). When the proxy and the backend parse a URL differently, the auth gate falls apart.
1. Path normalization quirks
The proxy inspects the raw URI; the backend normalizes it:
GET /admin/..;/dashboard
GET /admin/./dashboard
GET /admin/..%2f/dashboard
GET /admin;/dashboard
Nginx sees /admin/..;/dashboard as not matching the /admin ACL block, while a Tomcat/Spring backend strips /..;/ and serves the protected controller.
2. Reverse-proxy header overrides
Many internal stacks trust legacy routing headers:
GET / HTTP/1.1
Host: target.com
X-Original-URL: /admin/dashboard
X-Rewrite-URL: /admin/dashboard
X-Forwarded-Prefix: /admin
The WAF sees a benign request to /. The backend reads X-Original-URL, rewrites the internal route, and runs the admin logic.
3. Verb tampering and method override
Access control is often tied to a single method:
GET /admin/users ➔ 403
POST /admin/users ➔ 200
HEAD /admin/users ➔ 200
...or via override headers like X-HTTP-Method-Override: GET and a _method=PUT body field.
4. Case and extension tricks
GET /Admin/Dashboard
GET /admin/dashboard.json
GET /admin/dashboard/.
GET /admin/dashboard?x=1
How to actually fix it
- Normalize before you inspect. Canonicalize and decode the path before evaluating ACLs at the edge.
-
Strip client-supplied routing headers (
X-Original-URL,X-Rewrite-URL,X-Forwarded-*) at the ingress boundary so nobody can inject them. - Zero-trust the backend. Never rely only on the edge gateway. Every service validates identity and role on the controller itself.
Ever unlocked a critical finding through path normalization? Share your favourite bypass below.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.