# Caching a read

> Goal: serve a stable read (a catalogue, reference data) without a round trip to the upstream - without ever serving one caller what was answered to another.

Guide - Since version 1.2.0 - Flexway v1.6.3 - https://flexway.io/en/docs/operations-cache/

> Operations guide: configure, deploy and observe the gateway. What the capability does, product side: [product guide](https://flexway.io/en/docs/product-overview/); calling it: [developer guide](https://flexway.io/en/docs/developer-overview/).

**Goal**: serve a stable read (a catalogue, reference data) without a round trip to the upstream -
without ever serving one caller what was answered to another.

**Command** - in the contract, on the operation:

```yaml
paths:
  /products:
    get:
      x-flexway-cache: { ttl: 60s }                   # public: anonymous callers
  /me/cart:
    get:
      security: [ { customers: [] } ]
      x-flexway-cache: { ttl: 30s, scope: subject }   # one entry per tenant and subject
```

or, for a set of routes, a `cache` policy - always public:

```yaml
policies:
  definitions:
    catalog-cache: { type: cache, config: { ttl: 5m } }
  chains:
    routes: { "/products/**": [catalog-cache] }
```

**Check**: the second request answers `Flexway-Cache: hit` and an `Age`, and the upstream never sees
it; `flexway_cache_lookups_total{result="hit"}` goes up; `/config` shows `cache` in the catalogue, the
console a "cached" mark. An `If-None-Match` matching the stored `ETag` gets a `304` from the cache.

| Rule | What it means |
| --- | --- |
| Nothing by default | An operation without `x-flexway-cache` or a `cache` policy is never cached; the operation wins over the policy |
| The upstream shortens | Lifetime = the shortest of `ttl` and `max-age`, `s-maxage` or `Expires`; `no-store`, `no-cache`, `private`, `Set-Cookie`: never kept |
| Known length | A response without `Content-Length` is a stream to the gateway: relayed unbuffered, never kept |
| A hit counts | Rate and quota are counted before the cache is read: it spares the upstream, never the caller's allowance |
| Identity | An identified caller neither reads nor fills a public cache; an authenticated cache is declared by the operation (`scope: subject`), keyed by tenant **and** subject |
| Refused at load | A cache on a stream, a WebSocket, a gRPC method, inference, a write; a public one on an operation that requires an identity - the message names the operation |
| Memory | Per replica, bounded: `server.cache.max_bytes` (32 MiB), `server.cache.max_entry_bytes` (1 MiB); not reloadable; a reload purges it |

Full example: [Response cache](https://flexway.io/en/docs/operations-examples-gateway/#response-cache).
