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.

http
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.

http
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.

http
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.

http
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.

HeaderValueExample
OC-IPCountryISO 3166-1 alpha-2 country codeLB
OC-IPCityCity name (English)Beirut
OC-IPContinentTwo-letter continent codeAS
OC-RegionLargest subdivision name (state, governorate)Beirut
OC-TimezoneIANA time zoneAsia/Beirut
OC-IPLatitudeLatitude, decimal degrees33.8938
OC-IPLongitudeLongitude, decimal degrees35.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.

http
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.

http
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.

http
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.

http
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, Range and 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-Encoding is forwarded unchanged.
  • Cookie and Authorization are 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_session or $http_authorization) — see Handling Dynamic Requests. A cache rule can also strip Cookie before the request reaches the origin.
  • X-Real-IP, X-Forwarded-Proto, CDN-Loop and every OC- 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:

HeaderWhat OpenCache does
Set-CookieRemoved on cached routes, so a cookie is never stored and replayed to others. Kept on proxy routes.
ServerReplaced with opencache.
AgeReplaced with OpenCache's own value (see Age).
Alt-SvcReplaced with OpenCache's HTTP/3 advertisement on HTTPS sites.
Speculation-RulesRemoved only when it points at Cloudflare (/cdn-cgi/speculation). Other values pass.
NEL, Report-To, Reporting-EndpointsRemoved only when they carry Cloudflare's cf-nel reporting group. Your own policies pass.
Cache-Control, ExpiresReplaced 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.
DateSet 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

http
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.

http
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.

http
PF-Pop: 494176657117179905

PF-Server

The name of the OpenCache node that served the response.

http
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.

http
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).

http
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.

http
X-Cache-Status: HIT
ValueMeaning
HITServed from the cache.
MISSNot in the cache; fetched from the origin (or a deeper cache tier, see below).
EXPIREDIn the cache but expired; fetched again from the origin.
STALEExpired copy served because the origin was unreachable or erroring.
UPDATINGExpired copy served while a fresh one is fetched in the background.
REVALIDATEDExpired copy confirmed still valid by the origin (304 Not Modified) and served.
BYPASSA 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?".

http
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-StatusPf-Cache-Status parameters
HIThit
MISSfwd=miss
EXPIREDfwd=stale
REVALIDATEDfwd=stale; fwd-status=304
BYPASSfwd=bypass
STALE / UPDATINGhit 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.

http
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.

http
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:

FieldMeaning
cacheCache status, same values as X-Cache-Status. Always present; NONE outside cached routes.
fillWhere the bytes came from: edge (this node's cache, no fetch), a tier name, or origin.
ageSame as the Age header. Omitted when 0.
upstream_msTime to the origin's (or upstream tier's) response, in ms. Last attempt only.
routeRuleThe route that matched.
cacheRuleThe 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.

http
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.

http
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-Security header 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:

http
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:

http
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.

HeaderMeaning
Pf-Tier-Inner-StatusCache status inside the node's internal storage tiers, e.g. default:MISS.
Pf-Tier-Fallback: 1An internal storage tier was unavailable; the request was served by fallback.
Pf-Cache-IO-Fallback: 1A 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:

http
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:

CloudflareOpenCacheNotes
CF-Connecting-IPOC-Connecting-IPVisitor IP, single address
True-Client-IPOC-Connecting-IPAdd a custom header if you need this exact name
CF-IPCountryOC-IPCountryOmitted (not XX) when unknown
CF-IPCity, CF-Region, ...OC-IPCity, OC-Region, ...See Visitor location headers
CF-VisitorOC-Visitor{"scheme":"https"}
CF-Ray (request)OC-RIDPer-request ID for origin logs
CF-Ray (response)PF-Request-ID (+ PF-Pop, PF-Server)Quote it to support
CF-Cache-StatusX-Cache-StatusSame idea, nginx status values
CDN-LoopCDN-LoopSame standard header
/cdn-cgi/trace/oc-cgi/traceSame 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:

bash
curl -sS -o /dev/null -D - https://cdn.example.com/assets/app.js
http
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:

  1. x-cache-status: HIT and fill=edge — served from this node's cache; the origin was not contacted.
  2. pf-cache-age: 37 — this PoP has held the object for 37 seconds.
  3. routeRule / cacheRule — the route and cache rule that matched. If the wrong rule matched, check rule order and match conditions.
  4. 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:

bash
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