SiteError.comYour friendly guide to HTTP status codes
Status CodesBlog
  1. Home
  2. Blog
  3. Understanding HTTP 405 Method Not Allowed: The Required Allow Header and Why It's Not a 404

Understanding HTTP 405 Method Not Allowed: The Required Allow Header and Why It's Not a 404

August 10, 20268 min read
4xxClient Error

405 Method Not Allowed is one of the few status codes where the RFC doesn't just define the meaning — it mandates a specific header you have to send along with it. Most implementations skip that requirement, which turns a genuinely useful error ("here's what you can do at this URL") into a dead end that just says "no." This post covers what 405 actually promises, the Allow header it's required to carry, how it differs from 404 and 501, and how to return it correctly — including an NGINX gotcha that trips people up more than you'd expect.

What Is a 405?

A 405 tells the client that the URL exists and the server understood the HTTP method — it just doesn't support that method for this particular resource.

The 405 (Method Not Allowed) status code indicates that the method received in the request-line is known by the origin server but not supported by the target resource. The origin server MUST generate an Allow header field in a 405 response containing a list of the target resource's currently supported methods. — RFC 9110, Section 15.5.6

In plain English: you used the wrong HTTP method for this endpoint. The resource exists, but it doesn't support the method you tried — and the RFC doesn't leave the Allow header as a nice-to-have. It's a MUST: the server is required to tell you which methods are supported (e.g., Allow: GET, HEAD) in the same response.

Don't forget the Allow header — it's required by the RFC and helps clients understand what methods they should use. For example, if a resource is read-only, return 405 for POST/PUT/DELETE with Allow: GET, HEAD, OPTIONS.

The Allow Header Isn't Optional

This is the detail that separates a correct 405 from a merely functional one. A 405 without an Allow header technically violates the spec — it tells the client "wrong method" without telling it the right one, which defeats the entire point of the code existing separately from a generic 400.

The mental model: 405 is supposed to be self-correcting. A client (or a developer debugging in the browser) that hits a 405 should be able to read the response and immediately know what to try instead, without guessing or checking documentation. Skip the header and you've turned a helpful, actionable error into a plain "no."

405 vs 404 vs 501

Three codes can all show up when a request doesn't land where the client expected, and they answer different questions:

CodeMeaningUse when
404 Not FoundResource doesn't exist at this URLNo route or record matches, regardless of method
405 Method Not AllowedResource exists, method doesn'tThe URL is valid, but this particular resource doesn't support the method used
501 Not ImplementedServer doesn't support the method at allThe method isn't implemented anywhere on the server, independent of which resource was targeted

404 and 405 are the pair that's easy to conflate, because both can be triggered by hitting POST /users/42 when only GET /users/42 is defined — but they mean different things about what exists. 404 says the URL itself is unknown; 405 says the URL is known but this method isn't one of its supported operations. 501 is rarer and describes the server, not the resource: it means the method itself (say, a custom or exotic verb) isn't implemented anywhere, so no Allow header listing per-resource methods would even make sense.

Common Causes

The recurring shapes of a genuine 405:

  1. Using POST on a read-only endpoint — a resource that only supports GET/HEAD gets a write attempt.
  2. Trying DELETE when only GET is allowed — the client assumes a REST-y method exists that the server never implemented for that resource.
  3. Wrong HTTP verb for the resource — a general mismatch between what a client expects a resource to support (often based on convention) and what the server actually wired up.

How Frameworks Produce 405s Automatically

Some frameworks generate 405s for you, which is worth knowing so you don't duplicate the logic — or worse, silently disable it.

In the Next.js App Router, a Route Handler file only defines the HTTP methods it exports (GET, POST, PUT, etc.); if a request arrives using a method the file doesn't export, Next.js responds with 405 automatically, without any code in the handler. Next.js also auto-implements OPTIONS and sets a correct Allow header based on whichever methods you did export, unless you define OPTIONS yourself — so the RFC's Allow requirement is already satisfied for free in the App Router, as long as you don't override it with something that forgets the header.

Returning 405 Correctly

Express / Node.js

Express doesn't generate 405s automatically — an undefined method on a defined path just falls through to your 404 handler unless you add an explicit method guard:

app
  .route("/articles/:id")
  .get((req, res) => {
    res.json(getArticle(req.params.id));
  })
  .all((req, res) => {
    // Any method other than GET lands here
    res.set("Allow", "GET, HEAD").status(405).json({
      error: "method_not_allowed",
      message: `${req.method} is not supported on this resource.`,
    });
  });

app.route() scopes the .all() fallback to just this path, so it only catches methods you haven't already handled with .get(), .post(), etc. — it won't swallow requests to other routes.

Next.js App Router

// app/api/articles/[id]/route.ts
export async function GET(
  request: Request,
  { params }: { params: Promise<{ id: string }> },
) {
  const { id } = await params;
  return Response.json(await getArticle(id));
}
 
// No POST/PUT/DELETE exported — Next.js returns 405 automatically
// for any other method, with an Allow header derived from the
// methods this file does export.

If you need a custom body on the 405 instead of Next's default, export the other methods explicitly and return the response yourself:

export async function POST() {
  return Response.json(
    { error: "method_not_allowed", message: "This resource is read-only." },
    { status: 405, headers: { Allow: "GET, HEAD" } },
  );
}

NGINX

The instinctive approach — limit_except — is a common trap: it restricts which methods are allowed, but it returns 403 Forbidden on a rejected method, not 405, and it doesn't set an Allow header. To get a spec-correct 405 with the required header, check the method explicitly and route to a named error location:

server {
    location @method_not_allowed {
        add_header Allow "GET, HEAD" always;
        return 405;
    }
    error_page 405 @method_not_allowed;
 
    location /articles/ {
        if ($request_method !~ ^(GET|HEAD)$) {
            return 405;
        }
        # normal proxy_pass / handling for GET and HEAD
    }
}

The error_page redirect is what lets you attach the Allow header to the final 405 response — setting add_header directly inside the if block doesn't reliably survive NGINX's response-generation internals the way it does via error_page.

REST API JSON body

HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD
Content-Type: application/json
 
{
  "error": "method_not_allowed",
  "message": "DELETE is not supported on this resource. Supported methods: GET, HEAD."
}

Common Pitfalls

  1. Omitting the Allow header. It's a MUST in the RFC, not a nice-to-have — a 405 without it leaves the client guessing what it should have sent instead.
  2. Using NGINX's limit_except and expecting a 405. It returns 403, not 405, and adds no Allow header — use an explicit method check with error_page if you need a spec-correct response.
  3. Returning 404 instead of 405 when the resource exists. If the URL matches a real resource but the method doesn't fit, that's a 405 — collapsing it into a generic 404 hides useful information from the client.
  4. Letting the Allow header drift from what's actually implemented. If you hardcode Allow: GET, POST in one place while the handler logic changes elsewhere, the two can fall out of sync — derive the header from the same source of truth as the routing, where the framework allows it.
  5. Reaching for 501 instead of 405. 501 says the server doesn't implement the method anywhere; 405 says this resource doesn't support it. A resource that simply doesn't allow DELETE is a 405, not a 501.

Wrapping Up

405 is a status code that's supposed to hand the client the answer, not just the problem. The rules of thumb:

  • 405 means the resource exists, but this method isn't one of its supported operations
  • The Allow header listing supported methods is a MUST, not optional — skipping it violates the RFC
  • 404 is for unknown URLs; 405 is for known URLs with an unsupported method; 501 is for methods the server doesn't implement at all
  • Watch for framework/proxy shortcuts (like NGINX's limit_except) that don't actually produce a spec-correct 405

For more, see our pages on 405 Method Not Allowed, 404 Not Found, and 501 Not Implemented.

Full Reference

405 Method Not Allowed

The request method is known by the server but is not supported by the target resource.

Related Status Codes

🤨400Bad Request🔐401Unauthorized💳402Payment Required🚫403Forbidden🔍404Not Found🙅405Method Not Allowed🍽️406Not Acceptable🎫407Proxy Authentication Required⏰408Request Timeout⚔️409Conflict👻410Gone📏411Length Required❌412Precondition Failed📦413Payload Too Large📜414URI Too Long📼415Unsupported Media Type📖416Range Not Satisfiable😞417Expectation Failed🫖418I'm a Teapot🚪421Misdirected Request🤔422Unprocessable Entity🔒423Locked🎯424Failed Dependency⏰425Too Early⬆️426Upgrade Required🔑428Precondition Required🚦429Too Many Requests📋431Request Header Fields Too Large⚖️451Unavailable For Legal Reasons
Back to Blog

Popular Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 302 Found
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

Compare Codes

  • 401 vs 403
  • 301 vs 302
  • 404 vs 410
  • 500 vs 502
  • Compare any codes →

Categories

  • Informational
  • Success
  • Redirection
  • Client Error
  • Server Error
  • NGINX
  • Cloudflare
  • AWS ELB
  • Microsoft IIS

Tools

  • Cheat Sheet
  • Status Code Quiz
  • URL Checker
  • API Playground
  • Blog

© 2026 SiteError.com. All rights reserved.