# 418 I'm a Teapot: Origin and Real-World Use

> 418 I'm a teapot comes from the 1998 April Fools RFC 2324. See why RFC 9110 reserves it, why Node and Go keep it, and why some sites return it to bots.

Source: https://howhttpworks.com/status-codes/418
Last reviewed: 2026-10-04

> **TL;DR:** 418 is a joke status code from an April Fools RFC (2324, 1998). RFC 9110 reserves it as unused, so no standard behaviour exists. When you hit it on a real site, a human or a bot filter deliberately chose it, usually to turn you away as an automated client.

## Origin

RFC 2324 (1 April 1998) specified HTCPCP, a protocol for controlling coffee pots. It added methods `BREW` and `WHEN`, a `coffee:` URI scheme, and a status code: `418 I'm a teapot`, to be returned by a teapot asked to brew coffee. RFC 7168 (1 April 2014) extended it to tea, adding a `TEA` method, a `Safe` request header and tea-specific behaviours.

```http
BREW /pot-1 HTTP/1.1
Host: coffee.example.com
Content-Type: application/coffee-pot-command

start

HTTP/1.1 418 I'm a teapot
Content-Type: text/plain

Short and stout.
```

## Why it is still in your stack

In 2017 there was a proposal to drop 418 from libraries. Developers objected (the "Save 418" campaign), and maintainers of Node.js, Go (`http.StatusTeapot`), Python and others kept it. RFC 9110 §15.5.19 then settled the matter by listing it as `(Unused)`, which tells implementers the number is spent, not that it is meaningful.

Google keeps an Easter egg at `google.com/teapot`, and many frameworks use 418 in their test suites as a convenient "unusual 4xx" for checking that clients treat unknown codes in the 4xx class as client errors. The relevant rule is RFC 9110 §15: an unrecognised status code is treated like the x00 code of its class, so a client should handle an unknown 4xx as [400](https://howhttpworks.com/status-codes/400).

## What it means when you actually see it

The site returned 418 deliberately. The three realistic cases:

1. **Bot or scraper blocking.** Some sites and security layers reply 418 to traffic they have classified as automated: default library user agents (`python-requests/2.x`, `Go-http-client/1.1`), data-centre IP ranges, requests missing typical browser headers, or TLS fingerprints that do not match a real browser. Use the response to find out who sent it: look at the `Server`, `Via`, `Set-Cookie` (vendor cookies) and the body. The code itself carries no information.
2. **A developer's placeholder.** Some APIs return 418 for "I refuse to process this" without a better category. Read the body.
3. **A test or joke endpoint.** `httpbin.org/status/418` returns a teapot ASCII drawing.

```bash
curl -i https://httpbin.org/status/418
```

```http
HTTP/2 418
x-more-info: http://tools.ietf.org/html/rfc2324
content-type: text/plain

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
```

## If you control the server

Do not use 418 for real refusals. Proxies, CDNs, uptime monitors and SDKs have no defined behaviour for it, so they will not retry it, alert on it or cache it consistently. Use [403](https://howhttpworks.com/status-codes/403) for a refusal, [429](https://howhttpworks.com/status-codes/429) with `Retry-After` for throttling, and [451](https://howhttpworks.com/status-codes/451) for legal blocks. If you want the client to learn nothing, nginx's [444](https://howhttpworks.com/status-codes/444) closes the connection with no response.

## If you are the client

Check whether the same URL works in a browser. If it does, compare headers (`User-Agent`, `Accept`, `Accept-Language`) with a curl `-v` request. If the site's terms permit automated access, look for an official API or contact the operator, rather than escalating through header spoofing.

## Related

- [403 Forbidden](https://howhttpworks.com/status-codes/403): the real refusal code.
- [429 Too Many Requests](https://howhttpworks.com/status-codes/429): throttling.
- [444 Connection Closed Without Response](https://howhttpworks.com/status-codes/444): silent drop in nginx.
