HTTP status code · Client errors (4xx)
405 Method Not Allowed
405 Method Not Allowed means the server knows the URL but that resource does not support the HTTP method you used, such as a POST to a page that only accepts GET. The usual cause is a form or API call using the wrong method, or a route that simply does not define a handler for it.
| Class | 4xx, Client errors |
|---|---|
| Defined in | RFC 9110 §15.5.6 |
| Cacheable by default | Yes, heuristically cacheable; so a CDN may keep answering 405 after you add the method, until the cached copy expires |
| Safe to retry | Only with one of the methods listed in Allow |
| Relevant headers |
|
What 405 means
RFC 9110, section 15.5.6, defines 405 as a method that the origin server knows but the target resource does not support, and it makes the Allow header mandatory: the response must list the methods the resource does accept. A method the server does not recognize at all, such as a typo like PSOT, is a 501 instead.
Frameworks differ in whether they send it. Go 1.22+ ServeMux answers 405 with an Allow header automatically when a path matches but the method does not, and so do Next.js route handlers for methods the file does not export, and FastAPI. Express does not: an unmatched method falls through to its default 404 "Cannot POST /path".
A 405 is heuristically cacheable, which surprises people after a deploy. If a CDN cached a 405 for a URL before you added a POST handler, it can keep serving the stale error until the cache entry expires or you purge it.
Common causes
If you are visiting the site
- You submitted a form on a page that was cached or saved, and the site no longer accepts submissions at that address.
- You refreshed a page right after submitting a form and the browser resent the POST to a URL that only accepts GET.
If you run the server
- A form or fetch call posts to a static file: nginx answers 405 to POST on static content because it only serves files for GET and HEAD.
- The route handler is missing for that method, for example a Next.js route.ts that exports GET but the client calls PUT.
- A trailing-slash redirect (301 or 302) turned a POST into a GET, or the client followed a redirect to an endpoint that expects a different method.
- The hosting platform or a WAF blocks PUT, DELETE or PATCH by default, as some shared hosts do.
How to fix it
If you are visiting the site
- Go back to the site, reload the form page from its real address and submit again.
- Avoid refreshing a page that shows form results; navigate to it again instead.
If you run the server
- Read the Allow header of the response (curl -i -X POST https://example.com/path) to see which methods the server accepts, then match the client to it.
- Add the missing handler, or point the form action at the endpoint that processes it instead of at a static page.
- Use 307 or 308 for redirects in front of POST endpoints so the method is preserved, or make clients call the canonical URL directly.
- Purge the CDN cache for the URL after adding a method.
How to send 405
Go 1.22+ ServeMux, Next.js route handlers and FastAPI send 405 with Allow on their own when a path matches but the method does not; the examples show an explicit refusal. In nginx, limit_except with deny all answers 403, not 405.
app.delete('/invoices/:id', (req, res) => {
res.set('Allow', 'GET, HEAD');
res.status(405).json({ error: 'Invoices cannot be deleted, only voided' });
});// app/invoices/[id]/route.ts
export async function DELETE() {
return Response.json(
{ error: 'Invoices cannot be deleted, only voided' },
{ status: 405, headers: { 'Allow': 'GET, HEAD' } }
);
}mux.HandleFunc("DELETE /invoices/{id}", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Allow", "GET, HEAD")
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusMethodNotAllowed) // 405
w.Write([]byte(`{"error":"Invoices cannot be deleted, only voided"}`))
})from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.delete("/invoices/{id}")
def delete_invoice(id: str):
raise HTTPException(status_code=405, detail="Invoices cannot be deleted, only voided", headers={"Allow": "GET, HEAD"})location /api/reports/ {
if ($request_method !~ ^(GET|HEAD)$) {
add_header Allow "GET, HEAD" always;
return 405;
}
proxy_pass http://app;
}Commonly confused with
- 405 vs 501
- 501 means the server does not recognize the method at all; 405 means it knows the method but this resource does not allow it.
- 405 vs 404
- Express and some other frameworks answer 404 for a known path with an unhandled method, where 405 would be more accurate.
- 405 vs 403
- 403 refuses the request for policy reasons; 405 refuses only the method, and the Allow header shows what would work.
Frequently asked questions
- Why does nginx return 405 Not Allowed for POST requests?
- The request hit a location that serves static files, and nginx only serves files for GET and HEAD. Proxy the path to your application (proxy_pass or fastcgi_pass) or point the form at the right endpoint.
- Is the Allow header required in a 405 response?
- Yes. RFC 9110 says the origin server MUST generate an Allow header listing the methods the resource currently supports. It may be empty if the resource allows no methods at all.
- Why do I get 405 in a Next.js route handler?
- The route.ts file does not export a function for that method. Export an async function named after the method, such as POST or DELETE, and Next.js routes it.
- Should I return 405 or 404 for an unsupported method?
- Return 405 with Allow when the path exists; it tells the client exactly how to fix the call. Return 404 only when the path itself does not exist, or when you do not want to reveal it.
Last reviewed by Arielton Oberek.