Caching
Hitting the same endpoint twice is for people who enjoy watching paint dry. With caching on, a queued request that matches an earlier one gets the stored response without another trip to the upstream. It still becomes a job: the cache sits in the worker, between the queue and the upstream.
Turn it on
cache:
mode: "redis"
ttl: "10m"
redisKeyPrefix: "proxq:"
| Mode | Where entries live |
|---|---|
none | Nowhere. The default |
memory | An LRU inside the proxq process, capped at cache.maxEntries (default 10000). Lost on restart, not shared between replicas |
redis | The same Redis server as the queue, under cache.redisKeyPrefix (default proxq:). Shared by every proxq pointed at that Redis |
Entries expire after cache.ttl, 5m by default. The fields are listed on the configuration page.
What gets cached
- Any method. The same
POSTwith the same body is a hit, a different body is a miss. - Only
2xxresponses. A4xxor5xxis stored as the job result but not cached. - Only queued requests. Direct-proxied requests never touch the cache.
The replayed response from /content carries X-Cache-Status: HIT when it came from the cache and X-Cache-Status: MISS when the upstream answered. With caching off there is no such header.
Cache key
The key is a SHA-256 over the method, the full upstream URL, the request headers and the body. Headers in the exclude list are left out, so a header that changes on every request does not defeat the cache.
The default exclude list is X-Request-ID, X-Forwarded-For, X-Real-IP and X-Forwarded-Proto. Set cacheKeyExcludeHeaders on an upstream to replace it:
upstreams:
- prefix: "/api"
url: "http://backend:3000"
cacheKeyExcludeHeaders:
- "X-Request-Id"
- "X-Trace-Id"
The list replaces the defaults. Only the headers you name are excluded.
Names are compared exactly against Go’s canonical header form, in which each word starts with a capital and the rest is lower case: X-Request-Id, X-Real-Ip, X-Trace-Id. A name written another way, such as X-Trace-ID or x-trace-id, never matches. Two of the defaults, X-Request-ID and X-Real-IP, are not in canonical form, so those two headers still change the key unless you list X-Request-Id and X-Real-Ip yourself.
Authorization is part of the key unless you exclude it. Leave it in when different callers must not see each other’s responses.