Skip to content
Control Plane Labs

501 Not Implemented

Server error response defined by RFC 9110 §15.6.2.

Last updated July 27, 2026

Common causes at a glance

  • A request method the server has no implementation for at all
  • A reverse proxy rejecting a method its configuration does not permit
  • A client using PATCH, PROPFIND, or a custom verb against a plain static server
  • A proxy asked to handle CONNECT or an unsupported transfer coding

Reproduce it

curl -i -X PATCH -H 'Content-Type: application/json' -d '{}' https://api.example.com/v1/orders/1

What 501 Not Implemented means

The distinction that matters is 501 versus 405. 405 Method Not Allowed says “I know this method, but not here”: DELETE on a read-only collection, POST to a static file. 501 says “I do not implement this method anywhere, for anything”. A server that has never heard of PATCH returns 501. A server that implements PATCH on /orders but not on /orders/1/audit returns 405 with an Allow header.

Getting this backwards has a real consequence, because 405 requires that Allow header listing what the resource does support, whereas 501 correctly implies there is nothing to enumerate. The code also covers non-method cases, such as a proxy asked for a transfer coding it has no implementation for.

What the spec says

RFC 9110 §15.6.2 is explicit that 501 is the appropriate response when the server does not recognise the request method and is not capable of supporting it for any resource. It is also the only 5xx code that is heuristically cacheable, per RFC 9111 §4.2.2. That surprises people: an intermediary may reuse a 501 unless you send explicit cache directives. Contrast §15.5.6 for 405, which mandates Allow. Only GET and HEAD are required of general-purpose servers (§9.1); everything else is optional, and new methods are registered per §18.2.

What actually causes it

Static file servers are the usual source. nginx serving a directory answers GET and HEAD and rejects most else, so an API client pointed at the wrong hostname produces a confusing 501 rather than a 404. The other frequent case is a WebDAV or custom verb crossing an intermediary that only knows the standard set, since CDNs and WAFs often reject unknown methods outright. Inside frameworks, a 501 usually means the router matched nothing and the fallback handler picked the wrong code; most such responses should be 404 or 405.

How to debug it

Ask the server what it supports before guessing. An OPTIONS request costs nothing and separates the two failure modes immediately:

curl -sS -X OPTIONS -D - -o /dev/null https://api.example.com/v1/orders/1

If the response carries an Allow header, the server understands methods per-resource and your 501 is probably coming from a hop that never reached the application. If there is no Allow, the method is genuinely unimplemented. Repeat the request straight at the origin: a 501 that disappears when you skip the CDN is a proxy method allowlist, not an application limitation. Because 501 can be cached, purge the edge or add Cache-Control: no-store once you fix the route.

Working examples

curl

# OPTIONS separates "method unknown anywhere" from "method not allowed here".
curl -sS -X OPTIONS -D - -o /dev/null https://api.example.com/v1/orders/1

# Then the real method: read the status and any Allow header.
curl -sS -X PATCH -D - -o /dev/null \
  -H 'Content-Type: application/merge-patch+json' \
  -d '{"status":"shipped"}' \
  https://api.example.com/v1/orders/1

Python (requests)

import requests

url = "https://api.example.com/v1/orders/1"

opts = requests.options(url, timeout=10)
print("allow:", opts.headers.get("Allow"))

resp = requests.patch(url, json={"status": "shipped"}, timeout=10)
if resp.status_code == 501:
    print("method not implemented by this server at all")
elif resp.status_code == 405:
    print("method not allowed here; try:", resp.headers.get("Allow"))

Node (fetch)

const url = "https://api.example.com/v1/orders/1";

const opts = await fetch(url, { method: "OPTIONS" });
console.log("allow:", opts.headers.get("allow"));

const res = await fetch(url, {
  method: "PATCH",
  headers: { "Content-Type": "application/merge-patch+json" },
  body: JSON.stringify({ status: "shipped" }),
});
console.log(res.status, res.status === 501 ? "unimplemented" : res.headers.get("allow"));

Reference and tooling

The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.

Other 5xx codes