OpenCache HTTP headers
OpenCache is a reverse cache: it sits between your visitors and your origin. On the way in, it passes the visitor's request headers to your origin and adds a few of its own. On the way out, it passes your origin's response headers to the visitor, removes or replaces a small number of them, and adds headers that tell you how the request was served.
This page lists every header OpenCache adds, changes, or removes. For how caching decisions are made, see OpenCache Runtime Behavior.
Naming convention
OC-headers are sent to your origin (request headers).PF-/Pf-headers are sent to the visitor (response headers).- Both prefixes are reserved. Do not use them for your own headers: OpenCache
overwrites or strips them. A visitor cannot spoof an
OC-header your origin receives, because OpenCache sets those values itself.
Request headers
These are the headers your origin receives. OpenCache forwards the visitor's request headers as-is, except for the changes listed on this page, and adds the following.
Host
The hostname of your origin, as configured on the edge resource's upstream — not the hostname the visitor requested. This lets an origin that serves several sites route the request correctly.
Host: origin.example.com
Exception: upstreams configured as origin passthrough (fetch from whatever hostname the visitor requested) send the visitor's hostname.
OC-Connecting-IP
The visitor's IP address — the client that connected to OpenCache. Always a single address, IPv4 or IPv6.
OC-Connecting-IP: 203.0.113.42
This is the header to use when you need the visitor's IP. See Restoring visitor IP addresses.
OC-RID
A unique ID for the request, as 32 lowercase hexadecimal characters.
OC-RID: 9f2c1e7a4b8d4c0e8a5b3f1d2e4c6a8b
Log it on your origin to match origin log lines with OpenCache requests. The
visitor receives the same ID in PF-Request-ID on
single-node paths. On PoPs that use tiered storage or fill from another PoP,
the origin is contacted by an internal hop with its own ID, so OC-RID and
the visitor's PF-Request-ID can differ.
OC-Visitor
A small JSON object describing how the visitor reached OpenCache. Today it carries only the scheme.
OC-Visitor: {"scheme":"https"}
Value is {"scheme":"https"} or {"scheme":"http"}. New fields may be added
later, so parse it as JSON rather than comparing the whole string.
Visitor location headers
OpenCache looks up the visitor's IP address in a GeoIP database and sends the result to your origin.
| Header | Value | Example |
|---|---|---|
OC-IPCountry | ISO 3166-1 alpha-2 country code | LB |
OC-IPCity | City name (English) | Beirut |
OC-IPContinent | Two-letter continent code | AS |
OC-Region | Largest subdivision name (state, governorate) | Beirut |
OC-Timezone | IANA time zone | Asia/Beirut |
OC-IPLatitude | Latitude, decimal degrees | 33.8938 |
OC-IPLongitude | Longitude, decimal degrees | 35.5018 |
When a value is unknown (private addresses, or an address the database has no data for), that header is not sent at all rather than sent empty. Treat a missing header as "unknown".
X-Real-IP
The visitor's IP address. Same value as OC-Connecting-IP.
X-Real-IP: 203.0.113.42
X-Forwarded-For
The chain of client addresses. OpenCache appends the visitor's IP to any
X-Forwarded-For value the visitor sent.
X-Forwarded-For: 203.0.113.42
X-Forwarded-For: 198.51.100.7, 203.0.113.42
The left-most entries come from the visitor's request and can be set to anything by the visitor. Do not use them for access control.
X-Forwarded-Proto
The scheme the visitor used to connect to OpenCache: https or http.
Any value the visitor sent is overwritten.
X-Forwarded-Proto: https
On PoPs with tiered storage, the value is still the visitor's original scheme, not the scheme of the internal hop.
CDN-Loop
Loop protection (RFC 8586).
OpenCache appends an identifier of the node to any CDN-Loop value already
present on the request.
CDN-Loop: other-cdn, <opencache-node>
If a request arrives that already lists the same node — for example because
the origin points back at OpenCache — that node answers 503 instead of
forwarding it again. If your origin sits behind another CDN, forward
CDN-Loop unchanged so loops across both networks are caught.
Request headers OpenCache changes or drops
- Hop-by-hop headers (
Connection,Keep-Alive,TE,Upgrade,Transfer-Encoding) are not forwarded. OpenCache keeps its own persistent connections to your origin. WebSocket upgrades are not proxied. - On cached routes,
Rangeand the conditional headers (If-None-Match,If-Modified-Since,If-Match,If-Unmodified-Since,If-Range) are answered by OpenCache from the cached copy. Your origin receives a plain request for the full object on a miss. On non-cached (proxy) routes they are forwarded unchanged. Accept-Encodingis forwarded unchanged.CookieandAuthorizationare forwarded by default, on every route. They do not change the cache key and do not bypass the cache on their own. To keep personalized responses out of the cache, configure Bypass Lookup / Skip Store conditions (for example on$cookie_sessionor$http_authorization) — see Handling Dynamic Requests. A cache rule can also stripCookiebefore the request reaches the origin.X-Real-IP,X-Forwarded-Proto,CDN-Loopand everyOC-header sent by the visitor are overwritten or appended to, as described above.
Custom origin request headers
An edge resource can add its own request headers for the origin (for example
a shared secret your origin checks, or X-Forwarded-Host: $host). Values may
use nginx variables such as $host or $scheme. Setting a header to an empty
value stops it being sent, including the visitor's copy of that header.
X-Forwarded-Host and X-Forwarded-Port are not sent by default; add
them as custom headers if your origin needs them.
Restoring visitor IP addresses
To log or act on the visitor's real IP, read OC-Connecting-IP (or
X-Real-IP). Both always hold exactly one address, set by OpenCache.
Avoid parsing X-Forwarded-For for this: its left-most entries come from the
visitor and can be forged.
If your origin only accepts traffic from OpenCache, it is safe to trust these
headers. If your origin is also reachable directly, a client could send its
own OC-Connecting-IP; restrict the origin to OpenCache traffic or add a
custom shared-secret header (see above) and check it.
Response headers
Removed or replaced response headers
OpenCache passes your origin's response headers to the visitor, with these exceptions:
| Header | What OpenCache does |
|---|---|
Set-Cookie | Removed on cached routes, so a cookie is never stored and replayed to others. Kept on proxy routes. |
Server | Replaced with opencache. |
Age | Replaced with OpenCache's own value (see Age). |
Alt-Svc | Replaced with OpenCache's HTTP/3 advertisement on HTTPS sites. |
Speculation-Rules | Removed only when it points at Cloudflare (/cdn-cgi/speculation). Other values pass. |
NEL, Report-To, Reporting-Endpoints | Removed only when they carry Cloudflare's cf-nel reporting group. Your own policies pass. |
Cache-Control, Expires | Replaced when a cache rule sets browser expiration (see Cache-Control and Expires). |
Access-Control-* | Replaced by the edge resource's CORS settings when CORS is enabled. |
Date | Set by OpenCache. |
Vary is passed to the visitor but not used to split the cache. If a
response really depends on a request header, add that header to the cache key
(see Handling Dynamic Requests).
Other origin headers — ETag, Last-Modified, Content-*, Location, your
own X-* headers, any Strict-Transport-Security you send — pass through
unchanged. Headers configured on the edge resource can add, overwrite, or
remove response headers on top of this.
Added response headers
Server
Server: opencache
Present on every response.
PF-Request-ID
The unique ID of this request, 32 lowercase hexadecimal characters. Present on every response, including errors.
PF-Request-ID: 7f237f0a93a85f2288a08afaabfe3f9a
Include this ID when you report a problem to support — it identifies the exact request in OpenCache's logs.
PF-Pop
The ID of the OpenCache PoP that served the response.
PF-Pop: 494176657117179905
PF-Server
The name of the OpenCache node that served the response.
PF-Server: inf3-bey1-oix2
Age
How old the response is, in seconds, measured from when your origin generated
it (its Date header). Present on every response; 0 for responses that did
not come from your origin.
Age: 120
Pf-Cache-Age
How long, in seconds, this PoP has held the object in its cache. 0 on
anything that was not served from cache (MISS, BYPASS, EXPIRED).
Pf-Cache-Age: 37
Age and Pf-Cache-Age differ when the object was already old when OpenCache
fetched it — for example when your origin is itself a cache.
X-Cache-Status
Whether the response came from the cache. Present on cached routes only.
X-Cache-Status: HIT
| Value | Meaning |
|---|---|
HIT | Served from the cache. |
MISS | Not in the cache; fetched from the origin (or a deeper cache tier, see below). |
EXPIRED | In the cache but expired; fetched again from the origin. |
STALE | Expired copy served because the origin was unreachable or erroring. |
UPDATING | Expired copy served while a fresh one is fetched in the background. |
REVALIDATED | Expired copy confirmed still valid by the origin (304 Not Modified) and served. |
BYPASS | A bypass condition matched; fetched from the origin. |
The header is absent on proxy routes, redirects, diagnostic endpoints, and routes whose cache is disabled. A cache rule can rename it or turn it off.
On PoPs with several storage tiers (memory, SSD, disk), MISS only means the
first tier missed. Use Pf-Cache-Timing (fill=) or
Pf-Cache-Status to see whether the origin was actually
contacted.
Pf-Cache-Status
The standard Cache-Status
header (RFC 9211), under OpenCache's name. Opt-in per PoP; contact support
to enable it.
It lists one entry per cache layer that handled the request, closest to the origin first, so the first entry answers "did this reach the origin?".
Pf-Cache-Status: lhr01-memory; hit
Pf-Cache-Status: lhr01-hot; hit, lhr01-memory; fwd=miss
Pf-Cache-Status: lhr01-default; fwd=miss, lhr01-hot; fwd=miss, lhr01-memory; fwd=miss
X-Cache-Status | Pf-Cache-Status parameters |
|---|---|
HIT | hit |
MISS | fwd=miss |
EXPIRED | fwd=stale |
REVALIDATED | fwd=stale; fwd-status=304 |
BYPASS | fwd=bypass |
STALE / UPDATING | hit with a detail parameter |
Pf-Cache-Tier
The storage tier (or cache zone) selected for this request on cached routes,
for example memory, hot, or default. off when caching is disabled for
the route.
Pf-Cache-Tier: hot
This is the tier OpenCache looked in first. Which tier actually answered is
in Pf-Cache-Timing (fill=).
Pf-Cache-Timing
A per-node timing and cache summary in Server-Timing syntax. Present on every response.
Pf-Cache-Timing: inf3-bey1-oix2;dur=657;desc="cache=MISS&fill=origin&age=1&routeRule=route_default"
Format: <node>;dur=<ms>;desc="<fields>", where <fields> are &-separated:
| Field | Meaning |
|---|---|
cache | Cache status, same values as X-Cache-Status. Always present; NONE outside cached routes. |
fill | Where the bytes came from: edge (this node's cache, no fetch), a tier name, or origin. |
age | Same as the Age header. Omitted when 0. |
upstream_ms | Time to the origin's (or upstream tier's) response, in ms. Last attempt only. |
routeRule | The route that matched. |
cacheRule | The cache rule that matched, if any. |
dur is the time OpenCache spent on the request before sending headers, in
milliseconds. When the response passes through more than one OpenCache node,
each node appends its own entry, comma-separated, with the node closest to the
visitor last. Your browser's DevTools do not show this header in the timing
panel automatically, because it is not named Server-Timing; read it from
the response headers.
Alt-Svc
Advertises HTTP/3 so clients can switch to QUIC on their next connection. Present on HTTPS sites.
Alt-Svc: h3=":443"; ma=86400
Strict-Transport-Security
Added when HSTS is enabled in the edge resource's TLS settings, with the
configured max-age and includeSubDomains / preload flags.
Strict-Transport-Security: max-age=31536000; includeSubDomains
Current limitation. OpenCache's HSTS header is currently only reliably added to responses OpenCache generates itself, such as diagnostic endpoints. On cached routes the visitor receives your origin's
Strict-Transport-Securityheader if it sends one. Until this is resolved, also send HSTS from your origin if you rely on it.
Cache-Control and Expires
By default OpenCache passes your origin's Cache-Control and Expires to the
visitor unchanged.
When a cache rule sets browser expiration (Browser TTL, CDN TTL,
immutable, a directive such as public, or extra directives), OpenCache
replaces the origin's Cache-Control with one built from the rule:
Cache-Control: max-age=3600, public, s-maxage=43200, immutable
With a Browser TTL, OpenCache also sets Expires to the current time plus the
TTL. Enable Set Expires header on the rule to send your origin's Expires
instead.
What OpenCache stores is decided separately, by the rule's cache settings — see Cache Decisions.
CORS headers
When CORS is enabled on an edge resource, OpenCache sets the CORS headers itself on every response of that resource, replacing any your origin sends:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: Origin, Accept, Content-Type
Access-Control-Expose-Headers: X-Cache-Status, Content-Length, Content-Range
Access-Control-Expose-Headers and Access-Control-Allow-Credentials: true
are sent only when configured.
Preflight (OPTIONS) requests are answered by OpenCache directly with
204 No Content plus Access-Control-Max-Age (default 86400); they never
reach your origin.
Set one allowed origin, or *. When allowing credentials, use an explicit
origin, not * — browsers reject credentials with a wildcard.
Fallback and tiering markers
Diagnostic headers that can appear on PoPs with tiered storage. Their format may change; do not build on them.
| Header | Meaning |
|---|---|
Pf-Tier-Inner-Status | Cache status inside the node's internal storage tiers, e.g. default:MISS. |
Pf-Tier-Fallback: 1 | An internal storage tier was unavailable; the request was served by fallback. |
Pf-Cache-IO-Fallback: 1 | A cache disk read failed; the object was fetched again instead of erroring. |
Diagnostic endpoint headers
OpenCache serves built-in diagnostic endpoints under /oc-cgi/ on every site
— for example /oc-cgi/trace (who served you, over which protocol) and
/oc-cgi/health. Responses from them are never cacheable:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Pragma: no-cache
Expires: 0
They also carry Server, PF-Request-ID, PF-Pop, PF-Server, and
Pf-Cache-Timing (with cache=NONE). The /oc-cgi/ path is reserved; your
own routes cannot use it. See
Diagnostic Endpoints.
Cloudflare equivalents
If you are moving from Cloudflare, these OpenCache headers carry the same information:
| Cloudflare | OpenCache | Notes |
|---|---|---|
CF-Connecting-IP | OC-Connecting-IP | Visitor IP, single address |
True-Client-IP | OC-Connecting-IP | Add a custom header if you need this exact name |
CF-IPCountry | OC-IPCountry | Omitted (not XX) when unknown |
CF-IPCity, CF-Region, ... | OC-IPCity, OC-Region, ... | See Visitor location headers |
CF-Visitor | OC-Visitor | {"scheme":"https"} |
CF-Ray (request) | OC-RID | Per-request ID for origin logs |
CF-Ray (response) | PF-Request-ID (+ PF-Pop, PF-Server) | Quote it to support |
CF-Cache-Status | X-Cache-Status | Same idea, nginx status values |
CDN-Loop | CDN-Loop | Same standard header |
/cdn-cgi/trace | /oc-cgi/trace | Same key=value format |
If your origin is itself behind Cloudflare, Cloudflare's own response headers
(CF-Ray, CF-Cache-Status, ...) pass through to the visitor unchanged and
describe the origin side of the fetch, not OpenCache.
Debugging a request
Request the headers only, ignoring the body:
curl -sS -o /dev/null -D - https://cdn.example.com/assets/app.js
HTTP/2 200
server: opencache
pf-request-id: 7f237f0a93a85f2288a08afaabfe3f9a
pf-pop: 494176657117179905
pf-server: inf3-bey1-oix2
pf-cache-age: 37
age: 37
pf-cache-timing: inf3-bey1-oix2;dur=1;desc="cache=HIT&fill=edge&age=37&routeRule=route_default&cacheRule=static"
alt-svc: h3=":443"; ma=86400
x-cache-status: HIT
pf-cache-tier: hot
Reading it:
x-cache-status: HITandfill=edge— served from this node's cache; the origin was not contacted.pf-cache-age: 37— this PoP has held the object for 37 seconds.routeRule/cacheRule— the route and cache rule that matched. If the wrong rule matched, check rule order and match conditions.pf-server/pf-pop— the node and PoP that answered. Run the request again from another network to compare PoPs.
To see which node you reach and over which protocol, without fetching content:
curl -s https://cdn.example.com/oc-cgi/trace
When contacting support, send the PF-Request-ID, the full URL, and the time
of the request.
Further reading
- OpenCache Runtime Behavior — cache keys, cache decisions, stale serving, diagnostics
- Handling Dynamic Requests — bypass and skip-store conditions, cache keys by cookie or header
- Signed URLs — token-protected delivery
