Full site origins
A full site zone puts a whole website behind the CDN. A request a visitor makes with a cookie or a login goes straight through to your web server, the files your site serves identically to everyone are cached and shared, and a page asked for by a visitor with no cookie is cached only if your site says it may be. Point the zone at your server and a hostname of your own at the zone, and everything the site answers travels through.
What is cached on a full site#
A visitor sends their cookie with every stylesheet, script and image after their first page, so a rule that treated every cookie as "this answer is personal" would cache nothing. On a full site the rule reads the address as well as the cookie:
- A request for a static file is answered from a shared cache, whatever cookie it carries. A static file is one whose name ends in an extension from the list below. The copy we store is fetched without the visitor's cookie, so it is the one an anonymous visitor would have been given. The one exception is a request carrying an
Authorizationheader, which is passed to your server with it and never stored. - Any other request that carries a cookie or an
Authorizationheader goes to your server, every time, and its answer is never stored. - A page asked for without a cookie is fetched from your server and stored only if your site says so. With Cache Expiration Time on Respect Origin Cache-Control, that means a
Cache-Control: max-age(ors-maxage, or anExpires): a page with no cache headers is not stored, because a page that says nothing about caching usually says nothing by accident. With a fixed Cache Expiration Time, a page with no cache headers is stored for that time. Either way, a response that sets a cookie, or is markedprivate,no-storeorno-cache, or varies on a header we cannot key on, is never stored.
| Counted as a static file | Extensions |
|---|---|
| Stylesheets and scripts | css, js, mjs, cjs, map |
| Images | png, jpg, jpeg, gif, webp, avif, svg, ico, bmp, tif, tiff |
| Fonts | woff, woff2, ttf, otf, eot |
| Audio, video, manifests and segments | mp4, m4v, mov, webm, ogv, mp3, m4a, aac, ogg, oga, opus, wav, flac, m3u8, mpd, ts, m4s, vtt, srt |
| Downloads | pdf, zip, gz, tgz, bz2, xz, 7z, rar, apk, dmg, exe, msi, deb, rpm, iso, wasm, txt |
.html, .json and .xml are deliberately not on the list: a JSON answer is usually for one visitor and an HTML file is a page. The extension is read from the last segment of the path only, so /v1.2/api/me is a page and not a file.
The one thing to check: a file that is different for each visitor but sits at a file-shaped address, such as
/invoice.pdfor/export.zip, is fetched without the visitor's cookie and cannot be personal. Give it an address with no file extension,/invoices/2026-09or/export?id=42, and sendCache-Control: privatewith it: it then reaches your server with the visitor's cookie and is never stored.
What your server receives#
The request the visitor made: their hostname as Host, their cookies, their Referer, their content type and any other header your application reads. What is dropped: the connection's own headers, the whole X-Forwarded-* family the visitor sent (X-Forwarded-Port, X-Forwarded-Ssl and anything else with that prefix, not only the three we write ourselves), and any X-Real-IP or Forwarded; X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host are then written fresh by us. Any other header is forwarded as the visitor sent it.
- X-CDN-Connecting-IP: the visitor's address, exactly one. Use this one. We write it from the connection we accepted, so it cannot be forged: a request that reaches us already carrying it has been through us, and is refused as a loop.
- X-Forwarded-For: the same address, exactly one; anything the visitor sent in that header is removed rather than appended to.
- X-Forwarded-Proto and X-Forwarded-Host: whether the visitor came over http or https, and the hostname they used.
- Via: 1.1 CacheGenie-CDN, on every request, followed in brackets by the same signed marker CDN-Loop carries.
- CDN-Loop: our entry,
CacheGenie-CDNwith a zone, a time and a signature, after any other CDN's entry the visitor's request came with (RFC 8586). A request that comes back to us carrying it is a loop, a zone whose origin leads back to us directly or through a proxy of yours, and is answered with the 502 page at once. A proxy of yours in front of a hostname of ours should pass it on, or keep ourViaor ourX-CDN-Connecting-IP: any one of the three is enough. One that strips all three is still caught, by the address the request comes back from or, when that is not the address we reached it on, by a loop probe; and at any location a zone's requests may hold at most half of those waiting on origins, so even a loop nothing can show leaves every other site alone (see Limits). - A loop probe, now and then: up to three extra
GETs for a path we were already fetching, each withcdn-loop-probe=and a token on the end of its query string and the same token afterloop-probe/in itsUser-Agent. Two of them carry it in the path as well,cdn-loop-probe.and the token added as a part of the path at its end and before its last part, which your site answers with its 404 as it would any path it does not have; the path itself is asked for only when neither of those came back. We send them when a request to your origin has waited three seconds, when one address or your zone has many requests waiting on your origin at once, or when a request arrives naming us, to check that your origin does not lead back to us; answer them as you would any request. After a probe, a location does not send another for a minute.
Most frameworks need telling to trust a proxy before they will read these (Laravel's TrustProxies, Django's USE_X_FORWARDED_HOST, nginx's real_ip_header). Until you do, your logs, your country checks and your rate limits will all be about our edge rather than your visitors.
A fetch that fills the cache, for a static file or for a cookieless page, is made without any Cookie, Authorization or conditional headers, because the copy has to be the anonymous one.
Keeping your server's address private#
If you are putting your site behind us so that your web server cannot be reached directly, one header from your own application can undo it. We rewrite the ones that would: a Location, Content-Location, Refresh, Link or Content-Security-Policy naming your web server is rewritten to the hostname your visitor used, and any other response header that simply states your server's address is removed. A redirect to somewhere else entirely, such as a payment provider, is passed through untouched.
What we cannot rewrite is your own content. If your pages, your JSON or your sitemap contain absolute links back to your server's address, those are visible to anyone who looks. Use relative links, or your public hostname, for anything your site prints.
Firewalling your origin to us#
Rewriting headers stops us publishing your server's address; it does not stop anyone finding it, and the only real protection is your server refusing every connection that does not come from us. The addresses we connect from are published at https://cachegenie.com/edges.txt (also edges-ipv4.txt and edges-ipv6.txt, and GET /v1/edges on the API, which needs no key). Allow those and nothing else on your origin's port, and re-read the list once a day: an address is on it before the location behind it serves and stays on it until that location is retired.
Certificates on your server#
Your existing certificate keeps renewing after you point your DNS at us: a /.well-known/acme-challenge/ request we do not recognise is passed to your server, so certbot works exactly as it did, on a non-standard port with port 80 closed included. Three ways to arrange the origin's certificate, each with Verify Origin SSL Certificate on:
- An origin certificate: create one on the zone page and install it on your server (Origin certificates). Our servers trust it at any address and port and whatever hostnames the zone carries, and it does not expire, so there is nothing to renew.
- A separate origin hostname,
origin.example.com, that is not on the zone, with its own certificate renewed the ordinary way. The zone's Web Server Address is that name. - The server's IP address as the Web Server Address, with a publicly trusted certificate for the site. The certificate is then checked against the hostname the visitor used, while Forward Host Header is on (which a full site starts with).
Streaming, uploads and WebSockets#
Server-sent events, long-polling, streamed answers and WebSockets all work, for a visitor with a cookie and one without: an answer that streams reaches the visitor as it arrives and text/event-stream is never cached, and a WebSocket is relayed both ways once your server accepts it. Both stay open for as long as they carry something; Server-sent events and WebSockets has the details, including the keepalive to send.
Uploads pass through with no limit on their size and none on how long they take: the body goes to your server as it arrives and is never stored. An upload ends only if it goes quiet: 125 seconds with nothing arriving from the visitor is answered 408 and nothing is passed on, and 125 seconds in which your server takes nothing is answered with the 502 page. Uploads are not charged.
Your server has 125 seconds to start answering a page, an API call or anything else that is not a static file, with a cookie or without, counted from the moment the whole request has reached it. A static file fetched to be cached has 30 seconds. See Limits.
What a full site zone starts with#
Created as a full site, or switched to one, a zone is set up for a website: Forward Host Header on, Follow Redirects off, Strip Response Cookies off, Block Root Path Access off, Block POST Requests off, Add Canonical Headers off, and Cache Expiration Time on Respect Origin Cache-Control. On the zone page the save that switches the type applies all seven, whatever else the form showed, and the note above the button says so; through the API a value sent in the same call stands. Follow Redirects and Add Canonical Headers are then unused whatever they are set to: a redirect your site returns belongs to the visitor's browser, and your web server is not a canonical address. Three switches stay live and carry a warning, because each one breaks a website: Strip Response Cookies signs every visitor out, Block Root Path Access refuses the home page, and Block POST Requests breaks every form. See Zone settings.
A Cache Expiration Time set on a full site overrides the max-age your server sends, and stores a cookieless page that sent no cache headers for that time; a page marked no-cache, private or no-store stays uncached whatever the setting.
Checking it#
curl -sI https://www.example.com/ | grep -i x-cdn-cache
curl -sI https://www.example.com/app.css | grep -i x-cdn-cacheThe page reads MISS when it was fetched from your server and not stored, BYPASS when the request carried a cookie, and HIT only if your site sent a max-age for it. The stylesheet reads MISS once and HIT after that, cookie or no cookie. From the API the type is ORIGIN_TYPE SITE with the server as ORIGIN_URL; see Zones.