How to Fix a 405 Error (Method Not Allowed)
Find which methods the URL takes and what changed yours, with code for cURL, Python and Node.js.
Updated
TL;DR
A 405 error means the website or API knows the URL but refuses your HTTP method, such as a POST to a page that only reads data. Read the Allow header in the reply, then resend with a method it lists.
How servers and frameworks word a 405
The wording names the software that refused the method. Django sends no body, so Chrome draws its own page ending in HTTP ERROR 405.
| Where | What you see |
|---|---|
| nginx (POST to a static file) | 405 Not Allowed |
| Apache httpd (PUT or DELETE on a file) | Method Not Allowed The requested method PUT is not allowed for this URL. |
| Flask or Werkzeug | Method Not Allowed The method is not allowed for the requested URL. |
| FastAPI | {"detail":"Method Not Allowed"} |
| Laravel (exception message) | The GET method is not supported for route login. Supported methods: POST. |
| Django (server log, empty response body) | Method Not Allowed (POST): /contact/ |
| curl with -x set to a web server | CONNECT tunnel failed, response 405 |
Why this happens
The address exists, but it does not accept the kind of request you sent.
Each URL takes a set list of methods, the verb that opens every request, and the server turns down the rest. So a script or form that sends POST to a URL taking only GET is refused.
Diagnose your 405 first
The first two checks need no tools; the rest use DevTools or curl.
Note what triggered the 405; if it came right after a form submit or API call, that request used a method its address refuses.
Check whether the 405 page is a block notice with a request ID; if so, a firewall such as Alibaba Cloud WAF refused the request.
In DevTools Network, click the failed request; if the method under General is GET where you meant POST, something changed it on the way.
Check the browser console; 'It does not have HTTP ok status' means the browser's OPTIONS preflight, not your call, got the 405.
Run curl -v -x with your proxy on an https:// URL; a 405 to CONNECT came from the proxy address, not the site.
Solutions ranked by effectiveness
The first two fix your request; the third is for whoever runs the server.
- Most common fix
Send a method the URL accepts
Applies when the URL opens in a browser but your POST or DELETE gets 405. The code asks which methods the URL accepts without changing anything, so you can pick one for your client.
URL="https://example.com/api/items" for METHOD in OPTIONS GET; do echo "$METHOD:" curl -sS -o /dev/null -D - -X "$METHOD" "$URL" | grep -iE '^(HTTP|allow)' done - Check next
Stop your POST from turning into GET
Browsers and curl send GET unless told otherwise, and they switch POST to GET after a 301 or 302. Make the method explicit and target the final URL.
Add method="post" to the form tag when the handler expects POST.
Set the method option in fetch() for anything other than GET.
Send the request to the redirect's Location URL, with https:// and any trailing slash.
When curl -L must resend the POST after a redirect, add --post301 or --post302.
- Site owners
Let the method reach your app
If you run the endpoint and the method should work, add it to the route and make sure no static file rule or firewall answers first.
Register the method on the route; in a Django class-based view, add a post() method.
In nginx, proxy_pass POST to your app; the static handler answers only GET and HEAD.
Answer OPTIONS with a 2xx status and your CORS headers, as browser preflights require.
Look up the block page's request ID in your WAF console and suppress false positives.
Stop the 405 from coming back
Server settings that make 405s rarer and easier for clients to fix.
Return Allow with every 405 your app sends, as RFC 9110 requires, so clients can correct themselves.
Redirect URLs that take form posts or API writes with 307 or 308, which keep the method.
If a URL's methods may change, send 405s to GET with Cache-Control: no-store; caches may store them otherwise.
Related errors
Learn more
FAQ
Residential IPs in Postman and cURL
Add Geonode's HTTP/HTTPS proxy to either tool and send your API calls through it.


