HTTP 405 Method Not Allowed

Method Not Allowed

HTTP 405 is Method Not Allowed: the URL exists, but it doesn’t accept this method. RFC 9110 section 15.5.6 requires an Allow header listing the methods it does accept. An empty Allow is legal and means none at the moment (section 10.2.1), for instance when the resource is switched off by configuration.

The exchange

DELETE /api/invoices/88 HTTP/1.1

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

{"title": "Method Not Allowed", "status": 405, "detail": "Invoices can't be deleted; void them instead"}

The client should switch to a listed method, not retry.

Who sends it for you, and who doesn’t

  • Flask (through Werkzeug), Starlette and FastAPI, Django’s class-based views and @require_http_methods, and Go’s ServeMux with Go 1.22 method patterns like "GET /items/{id}" all answer 405 with Allow when the path matches and the method doesn’t.
  • nginx answers 405 Not Allowed to a POST on a static file.
  • Express doesn’t. An unmatched method falls through to the 404 handler as Cannot DELETE /api/invoices/88.
  • nginx’s limit_except doesn’t either. Its deny all answers 403.

405, 404 or 501

path unknown                                       404
path known, method not supported there             405 + Allow
method the server doesn't implement for any URL    501 Not Implemented

RFC 9110 section 15.6.2 assigns 501 to a method the server doesn’t recognize at all. A 404 for a wrong method sends developers hunting for a typo in a URL that is correct.

CORS preflights

A browser’s preflight is an OPTIONS request, and it fails unless the answer is a 2xx. A route that answers OPTIONS with 405 shows up in the console as a CORS error, never as a 405. Handle OPTIONS before method routing, or let a CORS middleware answer it.

Caching

405 is heuristically cacheable (RFC 9110 section 15.1). That only bites for a 405 to GET or HEAD, since caches don’t store responses to PUT or DELETE: a GET endpoint you enable later can keep answering 405 from a cache until it expires. Send Cache-Control with it if that matters.

In your stack

router.route('/api/invoices/:id')
  .get(showInvoice)                    // Express routes GET also answer HEAD
  .all((req, res) => res.set('Allow', 'GET, HEAD').sendStatus(405));
@require_http_methods(["GET", "HEAD"])    # Django: 405 with Allow for anything else
def invoice(request, pk): ...
mux.HandleFunc("GET /api/invoices/{id}", showInvoice) // Go 1.22+: a DELETE here gets 405 and Allow

Try it in the HTTP Status Codes

405 Method Not Allowed

  1. 405Method Not Allowed4xx Client error

    The address is real, but it does not answer to that method. The Allow header lists the methods it does accept.

    When to use it

    A POST to a read-only endpoint, or a DELETE on a resource that cannot be deleted.

    Common mistakes

    Returning 404 because the handler was never written, and leaving out Allow.

    Cacheable by defaultRFC 9110

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.