The Short Answer
Content delivery networks cache images, scripts and stylesheets out of the box, but not your pages. Cloudflare says it plainly: it does not cache HTML or JSON by default, and it decides by file extension, not by content type. To cache pages you add a rule that makes them eligible, give the edge copy a lifetime, tell browsers to check back every time, keep dynamic paths out of the rule, and clear the cache on every deploy. Done that way, the edge answers almost every request, the origin does a small fraction of the work, and nobody sees an old page after you publish.
The one sentence version
Cache pages at the edge for an hour, make browsers revalidate, exclude anything personal or dynamic, purge on every deploy, and expect your server logs to stop seeing most of your visitors.
When Caching Pages Is Safe
Page caching is safe when the same address returns the same page to everyone. Static sites, documentation, blogs and marketing pages usually qualify. Anything that changes per visitor does not: account pages, carts, pages that print a name, and any response that sets a cookie for the visitor. The test is simple. Open the page in two different browsers, one logged in if you have logins, and compare the HTML. If it differs, that path must stay out of the rule.
| Kind of page | Safe to cache at the edge? | Why |
|---|---|---|
| Static marketing pages and articles | Yes | Identical for every visitor |
| Redirects between address variants | Yes, with a lifetime | They change only when you change them |
| Not found and server error responses | No | A cached error outlives the fix |
| Account, cart and checkout pages | No | Personal to each visitor |
| Application programming interfaces and form endpoints | No | Dynamic answers and side effects |
| Pages that set a visitor cookie | No | The cookie would be served to everyone |
What We Measured on Our Own Site
We turned page caching on for our own site on 13 October 2026. The site is a static export, so every visitor gets the same HTML, and the origin was already fast, answering in about five milliseconds. So the gain was never going to be a dramatic speed number for one page. It was load, consistency and resilience. Comparing the seven days before with the four days after, from the origin server log:
| Measured at the origin, per day | 6 to 12 October | 14 to 17 October |
|---|---|---|
| Full page responses sent | 787 | 50 |
| Revalidations answered with no body | 2 | 683 |
| Page bytes sent | 19.6 MB | 1.1 MB |
| ChatGPT live agent page fetches the origin saw | 19.3 | 5.2 |
The first three rows are the point: the origin now sends about one twentieth of the page bytes it used to. The fourth row is the side effect most guides never mention, and it gets its own section below. Before the rule, the response header that reports cache status showed DYNAMIC on every page, meaning the network was not even trying to cache it. After, a fresh page shows MISS on the first request and HIT after that.
How to Set It Up, Step by Step
The steps below use Cloudflare cache rules, because that is what we run, but every CDN has the same four decisions: which requests, how long at the edge, how long in the browser, and how to clear it.
Confirm the pages are the same for everyone
Compare the HTML from two browsers and check that no page response sets a visitor cookie. Note every path that is personal or dynamic.
Create one cache rule for pages
Match your hostname and the page extensions: no extension, html, and any small data files your framework loads on navigation.
Exclude the dynamic paths
Leave out programming interface paths, webhook paths and the network control path, so form posts and tools always reach the origin.
Make matching requests eligible for cache
Choose eligible for cache, then set the edge lifetime to ignore the origin header and use a fixed time. We use one hour.
Set lifetimes by status code
Keep redirects for the same hour. Do not store not found or server error responses at all.
Make browsers revalidate
Set the browser setting to respect the origin, and have the origin send a no cache header on pages, so browsers ask again on every visit.
Purge on every deploy
Add a purge step to the deploy script after the build finishes, and fail the deploy loudly if the purge fails.
Verify from outside
Request a page twice and read the cache status header: MISS then HIT. Then load every asset the page references and confirm none is missing.
Step two has one detail for modern frameworks. Ours fetches small text files during navigation that describe the next page. We include that extension in the rule so the page and its navigation data are cached and cleared together. Caching one without the other can leave a visitor holding a page and navigation data from two different builds.
The Settings That Matter Most
| Setting | What we chose | Why |
|---|---|---|
| Match | Our hostname, page extensions, dynamic paths excluded | Pages only, nothing personal |
| Cache eligibility | Eligible for cache | Pages are not cached without it |
| Edge lifetime | One hour, ignoring the origin header | Short enough to be safe if a purge ever fails |
| Redirect lifetime | One hour | Redirects change only when we change them |
| Error lifetime | Not stored | A cached error would outlive its fix |
| Browser lifetime | Respect the origin, which says revalidate | No visitor keeps an old page after a deploy |
| Tiered cache | On | Fewer edge locations need to ask the origin |
Watch the zone default for browsers
Our zone had a default browser cache lifetime of four hours. Left alone, it would have told browsers to keep an old page for four hours after every deploy, and no purge reaches a browser. Respecting a no cache header from the origin fixed it.
Reading the Cache Status Header
Every response through the network carries a header that says what the cache did with it, and a second header, Age, that says how many seconds the edge copy has existed. Reading the two together is the fastest way to confirm the rule works and to debug it when it seems not to. These are the values we saw on our own pages before and after the change.
| Status you see | What it means | What to do |
|---|---|---|
| DYNAMIC | The request did not match any rule that makes it cacheable | Check the rule match: hostname, extension, excluded paths |
| MISS | Eligible, but no edge copy existed yet, so the origin answered | Request again; the second response should be a HIT |
| HIT | Served from the edge copy; Age shows how old it is | Nothing, this is the goal |
| REVALIDATED | The edge copy expired, the origin confirmed nothing changed | Normal once an hour per page with a one hour lifetime |
| A HIT right after a deploy | The purge did not run or did not finish | Check the deploy log and purge again |
One more check belongs in the verification step: request a page with a throwaway query parameter as well as without. Most setups include the query string in the cache key, so the parameter forces a MISS and shows you what the origin alone returns. If the two versions differ in anything other than the cache headers, something on the origin varies per request, and that page should not be cached.
Is It Worth It for a Small Site?
Honestly, the speed gain for a single page view can be small. When the origin already answers in a few milliseconds and the visitor is near it, the edge saves little time. The case for caching pages is elsewhere. The origin stops doing repeated work for identical requests, so a burst of crawlers or a link from a busy page no longer lands on one server. Visitors far from the origin get pages from a nearby copy. And the origin becomes a place you deploy to rather than a place every page view depends on. For us the twentyfold drop in page bytes was reason enough.
Purging Without Breaking Pages
The purge is the part that makes page caching safe, and it is also the part that can break things. A static build usually renames its script and style files on every deploy and deletes the old ones. If a cached page from the previous build survives, it points at files that no longer exist, and the page loads without its scripts or styles until the edge copy expires. That is why the edge lifetime should stay short, and why a failed purge must stop the deploy rather than be logged and ignored.
| Purge method | Use it when | Free plan note |
|---|---|---|
| Everything | After a full site build | Available, limited to a few requests a minute per account |
| Single address | One page changed outside a deploy | Available, generous limits |
| Prefix | A section changed, such as the blog | Available |
| Hostname | One of several hostnames changed | Available |
| Cache tag | Pages share a tag you set at the origin | Available |
Right after a purge every page is a miss, so the first visitor to each page waits for the origin and the origin briefly takes the full load again. For a small site that is seconds of extra work. For a large one, warming the most visited pages right after the purge, by requesting them once, spreads that load before real visitors arrive.
What Edge Caching Does to Your Crawler Logs
This is the side effect that catches people. Requests answered by the edge never reach your server, so they never reach your server log. If you measure search crawlers and AI agents from that log, as many teams do, your counts fall the day you turn caching on, even though nothing about the crawlers changed. On our site the ChatGPT live agent page fetches seen by the origin fell from 19.3 a day to 5.2, while the revalidation count rose by hundreds a day. The agents were being served from the copy.
Mark the date
Write down the day caching starts. Any chart that crosses that date compares two different measurements.
Count at the edge
After the change, take crawler and agent counts from the network analytics or request logs, which see every request.
Keep verifying names
Agent names are copied by scanners. Verification by published address ranges still applies, and it needs the real visitor address.
Search engines are unaffected in a useful way: they get the same page faster and more consistently, and your origin stops being the bottleneck when a crawler arrives in a burst. Our list of AI crawlers covers how to identify each agent, and the guide to JavaScript and AI crawlers explains why a cached, fully rendered static page is the easiest thing for any of them to read.
Common Mistakes
Caching everything
A rule without exclusions caches form endpoints, account pages and errors. Always list what stays dynamic.
Long edge lifetimes with no purge
A day long edge copy with no purge on deploy means a day of old pages after every change.
Forgetting the browser
A purge clears the edge, never the browser. Make browsers revalidate pages.
Reading the drop as a traffic loss
Origin logs fall when caching starts. Check the network analytics before reacting.
If you are also pinging search engines about new pages, the order matters: build, purge, then notify, so the engine fetches the new page and not a stale copy. Our IndexNow setup guide covers the notification step, and the technical SEO checklist has the rest of the deploy checks.
The Bottom Line
For a site whose pages are the same for everyone, caching HTML at the edge is one rule and one deploy step. Keep the edge lifetime short, keep errors and dynamic paths out, make browsers revalidate, and purge on every deploy with a hard failure if the purge fails. The origin will do a fraction of the work, pages will arrive consistently wherever visitors are, and your server log will stop telling you who visits. Move your counting to the edge on the same day you move your pages there.
