Skip to content
Control Plane Labs

418 (Unused)

Client error response defined by RFC 9110 §15.5.19.

Last updated July 27, 2026

Common causes at a glance

  • A framework or demo route deliberately returning the teapot as an easter egg
  • A WAF or bot filter using 418 as a deliberately meaningless rejection
  • A test fixture exercising unknown-status handling that leaked into a real environment
  • An HTCPCP implementation, which exists only as a joke

Reproduce it

curl -i https://api.example.com/v1/brew

What 418 (Unused) means

Reservation is the whole meaning. The IANA registry marks 418 unusable so that no future revision of HTTP can give it a real definition and collide with a decade of joke handlers already deployed. A 418 carries no protocol semantics.

That has a consequence for API design. Middleware, retry logic, and monitoring treat 418 as an unrecognised 4xx and fall back to generic client-error handling, the defined behaviour for any unknown status in that class. Returning it for a real condition means callers cannot distinguish it from a bug, so pick a code that exists.

What the spec says

RFC 9110 §15.5.19 states the position directly: RFC 2324 was an April 1 RFC lampooning the ways HTTP was abused, one abuse being an application-specific 418, and it has been deployed as a joke often enough to be unusable for any future purpose. It is therefore reserved in the IANA HTTP Status Code Registry, with a note that it could be reassigned if the 4xx space is ever exhausted. The original definition is RFC 2324 §2.3.2, the Hyper Text Coffee Pot Control Protocol, extended for tea by RFC 7168. Neither is Standards Track.

Where the teapot actually shows up

Every 418 in the wild is intentional. The common sources are easter-egg routes shipped by web frameworks, tutorial code nobody removed, and bot mitigation layers answering suspected scrapers with a code carrying no information, on the theory that an unknown status reveals less than a 403 about why the request was blocked. It also turns up in test suites as a status no real handler produces, which occasionally escapes into staging. If you are emitting it, the honest alternatives are 403 for a refusal, 422 for content you understood and rejected, or 429 for rate limiting.

How to debug it

There is nothing to diagnose in the protocol, so the work is finding who generated it. Check the Server header and body to distinguish an application easter egg from an edge appliance:

curl -i https://api.example.com/v1/brew | head -20

If the origin returns 200 for the same path while the edge returns 418, a bot filter or WAF rule is responsible and the rule needs adjusting rather than the application. If it comes from your own stack, grep the codebase for the literal 418; the route is usually a one-liner somebody added years ago. Client-side, treat any unrecognised 4xx as non-retryable and log the status verbatim so the oddity stays visible. The status code lookup confirms whether a code is registered before you build behaviour around it.

Working examples

curl

# Nothing special about the request; 418 is entirely a server-side choice.
curl -i https://api.example.com/v1/brew | head -20

# Compare edge against origin to find out which layer invented it.
curl -i --resolve api.example.com:443:203.0.113.10 \
  https://api.example.com/v1/brew | head -5

Python (requests)

import requests

resp = requests.get("https://api.example.com/v1/brew", timeout=10)
print(resp.status_code, resp.headers.get("Server"))

# 418 is reserved, so it carries no semantics. Do not build retry logic on it.
if resp.status_code == 418:
    print("reserved status; treat as a generic 4xx and do not retry")
    print(resp.text[:200])

resp.raise_for_status()  # requests raises HTTPError like any other 4xx

Node (fetch)

const res = await fetch("https://api.example.com/v1/brew");
console.log(res.status, res.headers.get("server"));

// Generic 4xx handling is the correct treatment for any unregistered code.
if (!res.ok) {
  const retryable = res.status === 408 || res.status === 429;
  console.log(`status ${res.status}, retryable: ${retryable}`);
  console.log((await res.text()).slice(0, 200));
}

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 4xx codes