# Default cache behavior

> Which requests the edge cache considers, which responses it stores, how long it keeps them and what it never caches.

The cache decides twice: first on the **request** (is it eligible, so the cache is looked up?), then on the **response** (can it be stored, and for how long?).

## Eligible requests

A request is looked up in the cache only if all of these hold. Otherwise it goes straight to the origin and is marked `DYNAMIC`.

- The cache is on for the name, and [development mode](https://developers.openwaf.dev/cache/configure/#development-mode) isn't.
- The method is `GET` or `HEAD`, without an `Upgrade` header (WebSocket) and without a request body.
- No `bypass` [cache rule](https://developers.openwaf.dev/cache/cache-rules/) matches.
- The path's extension is in the default list below, or a `cache` rule makes the request eligible.
- The path isn't ambiguous: no `;` path parameters, no encoded `/`, `\`, NUL, CR or LF, no backslash and no last segment ending in a dot (`/x.css.`).

The request's own `Cache-Control` and `Pragma` are ignored, so a client can't force the edge to refetch from your origin.

### Default extensions

```text
7z avi avif apk apng bin bmp bz2 class css csv doc docx dmg ejs eot eps exe flac gif gz ico iso jar
jpeg jpg js jxl m4a m4v mid midi mjs mkv mov mp3 mp4 ogg otf pdf pict pls png ppt pptx ps rar svg
svgz swf tar tif tiff ttf wasm webm webmanifest webp woff woff2 xls xlsx zip zst
```

plus `robots.txt`. HTML, JSON, XML, `txt` and source maps are **not** in the list: they need a `cache` rule.

## Freshness: how long a response is kept

The edge takes the TTL from the first of these that the origin sends:

1. `OpenWAF-CDN-Cache-Control`: a cache-control header for the OpenWAF edge only, never forwarded to browsers.
2. `CDN-Cache-Control` (RFC 9213), forwarded unchanged.
3. `Cache-Control`: `s-maxage`, then `max-age`.
4. `Expires`.
5. The defaults below, when none of the above is present.

`Cache-Control` is always forwarded to the browser as the origin sent it, unless you set a [browser TTL](https://developers.openwaf.dev/cache/configure/#browser-ttl).

### Default TTLs

Without any freshness header, on the default extension list only:

| Status | Edge TTL |
|---|---|
| `200`, `206`, `301` | 2 hours |
| `302`, `303` | 20 minutes, only for requests without `Cookie` or `Authorization` |
| `404`, `410` | 3 minutes |
| Anything else | not stored |

Known build directories of common frameworks get **7 days** instead of 2 hours: `/_next/static/`, `/_nuxt/`, `/_astro/`, `/_app/immutable/`, `/build/assets/` and `/packs/`, except deliberately mutable files such as `version.json`, `latest.json`, `sw.js`, `service-worker`, `manifest.json`, `page-data/` and `app-data.json`.

A path made eligible by a rule (HTML, JSON…) gets no default TTL: it needs freshness from the origin or a TTL in the rule.

No object is kept longer than 30 days.

## Responses that are never stored

These responses pass through to the visitor without being stored and are marked `BYPASS`:

- `Cache-Control: no-store` or `private`. With `private="Set-Cookie"`, the response is stored without that header.
- A `Set-Cookie` header, unless a rule overrides the edge TTL (then `Set-Cookie` is removed from the stored copy).
- A request with `Authorization` whose response doesn't say `public`, `s-maxage` or `must-revalidate`.
- `Vary` naming anything other than `Accept-Encoding`, `Accept` (images only) or `Origin`, including `Vary: *` and `Vary: Cookie`. See [Cache keys](https://developers.openwaf.dev/cache/cache-keys/#vary).
- A status that can't be cached, a body larger than the [maximum object size](https://developers.openwaf.dev/cache/configure/#maximum-object-size) (100 MB by default) or a content encoding other than gzip or Brotli.
- Personal documents: for `pdf`, `doc(x)`, `xls(x)`, `ppt(x)`, `csv`, `zip`, `7z`, `rar`, `tar` and `gz`, a request with `Cookie` or `Authorization` is stored only if the origin sent explicit freshness.
- Pages generated by the edge itself: challenges, block pages, the access gate and redirects.

### Cache deception armor

On by default. When the URL's extension promises one type but the origin answers `text/html`, `application/json`, `application/xhtml+xml` or `text/plain` (except `robots.txt` and `.csv`), the response isn't stored. This stops the classic attack where `/account.php/x.css` returns a personal HTML page that would otherwise be cached and served to others. Harmless mismatches such as `application/octet-stream` or a `.jpg` served as `image/webp` are allowed.

## Revalidation and stale content

- `no-cache`, `max-age=0` or `s-maxage=0` with an `ETag` or `Last-Modified`: the response is stored and revalidated with the origin on every use (`If-None-Match` / `If-Modified-Since`); a `304` saves sending the body again.
- An expired object with validators is revalidated: `REVALIDATED` on a `304`, `EXPIRED` on a new `200`.
- **Stale while revalidate**: with the origin's `stale-while-revalidate`, an expired object is served (`UPDATING`) while it refreshes in the background.
- **Stale if error**: when the origin fails (network error, timeout, `500`, `502`, `503`, `504`), the expired object is served (`STALE`) for the origin's `stale-if-error` or, without it, up to 24 hours by default.
- `must-revalidate`, `proxy-revalidate`, `no-cache` and `s-maxage` disable stale content.

## Range, HEAD and concurrent misses

- A `GET` with a single `Range` on a miss fetches the whole object once, stores it and answers the range as it arrives (`206`), so videos are cached too.
- A `HEAD` is served from the cache when the object is there; otherwise it goes to the origin and nothing is stored.
- When many visitors ask for the same missing object at once, only one request goes to the origin; the others wait up to 5 seconds and are served from the result.

## Compression

The edge asks the origin for `br, gzip` and keeps one copy per URL in the encoding the origin chose. A visitor whose browser doesn't accept that encoding gets the copy decompressed on the fly; `Vary: Accept-Encoding` doesn't split the cache.
