How HTTP Works
4xx · Client error
< HTTP/1.1 405 Method Not Allowed

405 Method Not Allowed

405 Method Not Allowed means the URL exists but not for that method. Read the Allow header, then fix redirects turning POST into GET, static hosting and WAFs.

Reviewed 4 min readbeginner4 sourcesTry itMarkdown
Cacheable
Yes, by default
Retry?
No: use a method listed in Allow
Usually sent by
Origin app or web server
Spec
RFC 9110 §15.5.6
Often confused with
501, 404
On this page

TL;DR: 405 Method Not Allowed means the URL exists but does not accept the HTTP method you used, and the response must list the accepted methods in an Allow header. The usual real-world causes are a form or fetch using the wrong verb, a redirect that turned POST into GET, static hosting that only serves GET, and a proxy or CORS layer blocking PUT/DELETE.

RFC 9110 section 15.5.6: the method “is known by the origin server but not supported by the target resource”, and the server “MUST generate an Allow header field”. Compare 501, which means the server does not recognize the method at all, and 404, where the path itself is unknown.

HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json

{"error":"method_not_allowed","allowed":["GET","HEAD","OPTIONS"]}

Who sent it?

EvidenceLayer
Allow: header lists methods and body comes from your framework (Method Not Allowed, Flask: The method is not allowed for the requested URL.)Application route does not define that method
nginx page 405 Not Allowednginx serving a static file (nginx static handling accepts only GET/HEAD, and POST to a static file returns 405)
405 Method Not Allowed with Server: AmazonS3S3 website endpoint or bucket policy blocks the verb
Server: cloudflare, CF-RayCloudflare Pages/Workers or a WAF rule restricting methods
IIS: HTTP Error 405.0 - Method Not Allowed or 405.0 ... The page you are looking for cannot be displayed because an invalid method (HTTP verb) is being usedIIS; frequently WebDAV module intercepting PUT/DELETE
Spring: Request method 'POST' is not supported (HttpRequestMethodNotSupportedException)Spring MVC mapping
Django: Method Not Allowed (POST): /path/ in the server logView class lacks post()

Fix it, most common first

  1. Read the Allow header and use one of those methods: curl -si -X POST URL | grep -i '^allow', or ask with curl -si -X OPTIONS URL.
  2. A redirect changed your method. 301 and 302 make most clients re-issue POST as GET, and the target (say a GET-only page) answers 405. Typical triggers: http to https redirect, www to apex, trailing slash redirect. Call the final URL directly, or have the server use 307/308, which preserve the method.
  3. Static hosting. POSTing a form to a page on GitHub Pages, S3 static hosting, Netlify without a function, or nginx try_files serving a file returns 405. Point the form to a real endpoint.
  4. A framework route only handles some verbs. Add the handler. Next.js App Router needs an exported function named exactly POST, PUT and so on in route.ts; Express needs app.post(...), and a GET-only app.get yields Cannot POST /path (404 from Express, not 405).
  5. Proxy or WAF restrictions. limit_except GET { deny all; } in nginx returns 403, not 405; but ModSecurity and CDN rules often return 405 for PUT, DELETE and PATCH. Allow the verbs for that path.
  6. CORS preflight. An OPTIONS request that your server answers with 405 breaks cross-origin PUT/DELETE/JSON POST. Handle OPTIONS and return Access-Control-Allow-Methods (see CORS preflight debugging).
  7. HTML forms only do GET and POST. <form method="put"> is treated as GET. Use fetch, or a hidden _method override your framework understands.
  8. HEAD or OPTIONS rejected. Monitoring tools and crawlers send HEAD; a handler that omits it returns 405 and trips health checks. Most servers should accept HEAD wherever GET is accepted (RFC 9110 section 9.1).

Fix by stack

nginx

# POST to a location that serves static files => 405. Route it to the app instead.
location /api/ {
    proxy_pass http://127.0.0.1:3000;
}

# Optional workaround for legacy POST-to-static
error_page 405 =200 $uri;

Apache

# Restrict verbs: denied methods get 403, not 405, unless you return 405 yourself
<LimitExcept GET POST OPTIONS>
    Require all denied
</LimitExcept>

Express / Node.js

app.route('/api/items')
  .get(listItems)
  .post(createItem)
  .all((req, res) => {
    res.set('Allow', 'GET, POST, OPTIONS').status(405).json({ error: 'method_not_allowed' })
  })

Next.js, Flask, Django

// app/api/items/route.ts : Next.js App Router
export async function GET() { return Response.json([]) }
export async function POST(req: Request) { return Response.json(await req.json(), { status: 201 }) }
// Any other method gets 405 with an automatic Allow header.

Flask returns 405 with an Allow header automatically when methods=[...] does not include the verb. Django class-based views return HttpResponseNotAllowed using http_method_not_allowed; DRF returns {"detail":"Method \"DELETE\" not allowed."}.

Reproduce and verify

curl -si -X DELETE https://api.example.com/items | sed -n '1p;/^[Aa]llow/p'
curl -si -X OPTIONS https://api.example.com/items | grep -i '^allow'
curl -sv -L -d 'a=1' http://example.com/form 2>&1 | grep -E '^> (POST|GET)'

The last command shows whether the second request in a redirect chain is still POST. Plain curl -L -d switches to GET after 301/302 unless you pass --post301/--post302, which is also what browsers do.

405 lives next to 404, 501, and 400. Method semantics: GET, POST, PUT, DELETE, OPTIONS. Try a bad verb in the request builder.

Frequently asked questions

What does 405 Method Not Allowed mean?

The target resource exists and the method is recognized, but the resource does not support that method (RFC 9110 section 15.5.6). The response must carry an Allow header listing the supported methods.

Why do I get 405 when POSTing to a static page or form?

Static hosts such as S3 website hosting, GitHub Pages and nginx serving files only accept GET and HEAD, so a POST gets 405. Send the form to a real endpoint such as a server route or serverless function.

Why does my POST turn into a GET and return 405 after a redirect?

Browsers and curl change POST to GET after 301 and 302 responses, so the second request hits a GET-only URL. Call the final URL directly, or redirect with 307 or 308, which preserve the method.

What is the difference between 405 and 501?

405 means the server knows the method but the resource does not support it, and requires an Allow header. 501 means the server does not recognize or implement the method at all.

Why does an OPTIONS request return 405 in my CORS setup?

The server has no handler for the preflight OPTIONS request, so cross-origin PUT, DELETE and JSON POST requests fail in the browser. Add an OPTIONS handler returning Access-Control-Allow-Methods and Access-Control-Allow-Headers.

Sources

  1. MDN Web Docs: 405 Method Not Alloweddeveloper.mozilla.org
  2. RFC 9110 section 15.5.6: 405 Method Not Allowedrfc-editor.org
  3. RFC 9110 section 10.2.1: Allowrfc-editor.org
  4. Next.js: Route Handlersnextjs.org

Keep going

Browse /search