# Warning Header

> Learn about the deprecated Warning header that provided additional status information about message transformations. Understand why it was removed from HTTP.

Source: https://howhttpworks.com/headers/warning
Last reviewed: 2026-10-05

> **TL;DR:** `Warning` is obsolete. RFC 9111 dropped it, so stop generating it and use `Cache-Control`, `Age`, and `Cache-Status` instead. If you see it in old logs, `Warning: 110` meant a cache served a stale response, and `214` meant a proxy transformed the body.

## What is Warning?

Warning carried information the status code couldn't, such as a cache serving stale content or a proxy transforming the body. [RFC 9111 §5.5](https://www.rfc-editor.org/rfc/rfc9111#section-5.5) **obsoletes** it. If you see it in a response today, it comes from legacy software or an application-specific convention; current HTTP caching doesn't require it.

## Historical Usage

Headers and output on this page are examples. A typical legacy response:

```http
Cache-Control: max-age=60
Age: 90
Warning: 110 cache.example.com "Response is Stale"
```

The response is 30 seconds past its freshness lifetime, and the warning just says so; it never gave the cache permission to serve stale. Current caches follow the rules in [RFC 9111 §4](https://www.rfc-editor.org/rfc/rfc9111#section-4), including applicable directives and validation requirements.

Whether a cache serves stale or fails the request depends on its stale-serving policy and the response's directives. Warning never decided that. A response with `Cache-Control: no-cache`, for example, still needed validation before reuse, and adding `Warning: 110` couldn't change that. When you remove a Warning generator, review the cache's reuse policy as a separate task.

Successful validation could strip an old `110` while keeping the same stored body. A transformation warning such as `214` followed different rules and could stick around. In archived traces, compare the response before and after validation; warnings weren't always removed together, and a changed warning didn't always mean a new body.

## Syntax

The historical syntax, for reading old logs:

```text
Warning: warn-code warn-agent "warn-text" ["warn-date"]
```

The code was three digits. The agent named the sender, or a pseudonym for it. The text was quoted. An optional quoted HTTP date, if present, had to match the response's `Date`.

Here's one with a matching date:

```http
Date: Mon, 05 Oct 2026 10:00:00 GMT
Warning: 199 proxy.example.com "Miscellaneous Warning" "Mon, 05 Oct 2026 10:00:00 GMT"
```

## Warning Codes

The [MDN legacy reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Warning) records these meanings:

- `110`: stale response.
- `111`: attempted revalidation failed.
- `112`: disconnected operation.
- `113`: both heuristic freshness lifetime and response age greater than 24 hours.
- `199`: miscellaneous warning.
- `214`: transformation applied.
- `299`: miscellaneous persistent warning.

These are Warning codes, not HTTP status codes. 1xx warnings were removed after successful validation; 2xx warnings could stay.

## Why It Was Deprecated

According to RFC 9111, few implementations generated it, even fewer showed it to users, and the heuristic-expiration problem it was partly designed for went away. The RFC never says the implementations were broken; the header just wasn't earning its keep. Current HTTP has no requirement to flag stale reuse.

## Modern Alternatives

[Cache-Control](https://howhttpworks.com/headers/cache-control) controls reuse, and [Age](https://howhttpworks.com/headers/age) tells you how old a response is. Between them they cover most of what `110` and `113` used to say, though not every old code.

For cache diagnostics, [RFC 9211](https://www.rfc-editor.org/rfc/rfc9211) defines `Cache-Status`:

```http
Cache-Status: edge; hit
```

It reports what the cache did, not an application error. Vendor headers such as [X-Cache](https://howhttpworks.com/headers/x-cache) each have their own meanings. No standard maps Warning codes one-to-one onto custom headers or `Server-Timing`.

## Legacy Support

Before removing a Warning generator, check whether any client actually reads it. You don't need to implement the old code lifecycle for current HTTP caching; it's obsolete. Keep cache policy and application error reporting as separate concerns.

If a legacy origin sends Warning and you've confirmed nothing reads it, nginx can strip it in your existing proxy location:

```nginx
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_hide_header Warning;
}
```

[`proxy_hide_header`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_hide_header) only hides fields coming from the upstream. Freshness and stale serving stay as they were, and a cache further downstream may add its own Warning. Compare a direct origin request with one through nginx to see which component is still sending it.

If your application needs to report partial results, put that in the response body. A JSON API might return a `warnings` array alongside its result. That array is your own contract, not the old HTTP field, so document what each code means and how clients should handle missing data. Copying the old three-digit codes into a new field without defining what they mean isn't a migration.

## Testing (for Legacy Systems)

Test with GET; a HEAD request may take a different cache path:

```bash
curl -sS -D - -o /dev/null https://example.com/resource
```

Record `Warning`, `Date`, `Age`, and `Cache-Control` together. A missing Warning tells you nothing about freshness or failed validation, since current caches aren't expected to send it.

For an origin reachable locally behind a public proxy, compare the two paths:

```bash
curl -sS -D /tmp/origin.headers -o /dev/null \
  http://127.0.0.1:3000/resource
curl -sS -D /tmp/edge.headers -o /dev/null \
  https://example.com/resource
```

Send the same Host and authentication on both requests if your deployment needs them. A Warning that appears only at the edge tells you something downstream of the origin adds it, though you'll still need to find out which component. Keep the raw values: multiple warnings and quoted dates trip up a naive split or regex.

In an old log, `214` meant something transformed the body. It says nothing about bytes saved or latency. `111` meant revalidation failed, and it's not the origin's final HTTP status. Check the accompanying status, validators, and proxy error logs before blaming either code on a specific backend failure.

## Related Headers

- [Age](https://howhttpworks.com/headers/age)
- [Cache-Control](https://howhttpworks.com/headers/cache-control)
- [X-Cache](https://howhttpworks.com/headers/x-cache)
