Browser Cache and Cloudflare Cache Are Not the Same: Cache-Control, Edge TTL, and Publish Refresh

A practical explanation of browser cache versus Cloudflare edge cache, covering Cache-Control, max-age, s-maxage, Edge TTL, Browser TTL, purge all, and cache settings for static blogs.

When configuring Cloudflare cache, the easiest mistake is mixing up two things:

  • How long Cloudflare edge nodes cache content.
  • How long the user’s browser caches content locally.

Both are caches, but they control different layers and carry different risks.

For a static blog, a stable approach is: make Cloudflare edge cache more aggressive, and keep browser cache more conservative. Cloudflare cache can be purged through the dashboard or API, while a user’s local browser cache cannot be cleared directly by the site owner.

Start With the Three Layers

A request roughly goes through three layers:

1
2
3
4
5
Browser
Cloudflare edge node
VPS / Nginx / Hugo static files

There are two cache layers that matter most:

  • Browser cache: cache stored locally on the user’s machine.
  • Edge cache: cache stored on Cloudflare edge nodes.

The Cache-Control returned by the origin Nginx affects the browser and may also affect the CDN. Cloudflare Cache Rules can separately set Edge TTL and Browser TTL. Response Header Transform Rules can also rewrite the final response headers returned to the browser. Without separating these layers, it is easy to think “cache is configured” when only one part is actually controlled.

Who Does Cache-Control Control?

Cloudflare’s learning center explains Cache-Control as an HTTP response header that tells browsers and intermediate caches how to handle resources.

A common example:

1
Cache-Control: public, max-age=300

This means the response can be cached, and the browser can treat it as fresh for up to 300 seconds.

1
Cache-Control: private

This means the response is suitable only for private caches such as browsers, not shared caches such as CDNs.

1
Cache-Control: no-store

This means do not cache the response anywhere. It is suitable for highly sensitive data.

1
Cache-Control: no-cache

The name is misleading. It does not mean “never cache.” It means the cached version must be validated with the server before use, usually with ETag or Last-Modified.

1
Cache-Control: s-maxage=86400

s-maxage is for shared caches such as CDNs. It can let the CDN cache longer while the browser follows max-age or other rules.

So Cache-Control is not only for browsers, and it is not only for CDNs. It is a set of cache instructions. Which layer follows or overrides them depends on the intermediate configuration.

Edge TTL Versus Browser TTL

In Cloudflare, the two TTL settings should be read separately.

Edge TTL decides how long Cloudflare edge nodes cache content.

For example:

1
Cloudflare Edge TTL = 1 day

This means Cloudflare can keep pages, images, CSS, JS, and other content at the edge for one day. When visitors request the same URL again, Cloudflare can return it directly if the cache hits, without going back to the VPS.

Browser TTL decides how long the browser caches content.

For example:

1
Browser TTL = 5 minutes

This means after receiving a resource, the browser can treat it as fresh locally for up to 5 minutes. Within those 5 minutes, it may use the local cache directly. After that, it needs to revalidate or request the resource again.

The key difference is:

  • Edge cache can be purged by the site owner.
  • Browser cache lives on the user’s computer, and the site owner cannot directly clear it.

That is why HTML pages should not get a long browser TTL. Otherwise, even after Cloudflare is purged, a visitor’s browser may keep showing an old local copy.

Why Static Blogs Can Cache HTML

Dynamic sites usually cannot blindly cache HTML because pages may contain login state, user profiles, carts, recommendations, or permission-specific content.

A static blog is different. Hugo-generated pages are usually static files:

  • Homepage.
  • Article pages.
  • Category pages.
  • Tag pages.
  • Multilingual pages.
  • RSS.
  • sitemap.

These responses are mostly the same for every visitor, so Cloudflare can cache the HTML. This reduces origin requests and improves access speed from different regions.

The key is:

1
2
Cloudflare Edge can be longer
Browser cache should be shorter

For example:

1
2
Cloudflare Edge TTL = 1 day
Browser TTL = 5 minutes

Cloudflare can cache for one day, while each user’s browser only caches for five minutes.

If Every Publish Purges Everything, Edge TTL Can Be Longer

If the deployment script calls Cloudflare purge everything every time, Edge TTL can be set longer.

The logic is:

1
2
3
Normal time: Cloudflare caches for 1 day to reduce origin traffic
Publishing: upload the new public directory and purge all
After publishing: Cloudflare fetches fresh content from origin

This model fits static blogs because content changes are not real-time, and updates happen during deployment.

Edge TTL can be 1 day or 1 month. The difference is mainly risk preference:

  • 1 day: if purge fails, stale content naturally expires within a day.
  • 1 month: higher hit rate, but stale content can stay longer if purge fails.

For a personal blog, 1 day is a safer starting point.

Why Browser TTL Should Not Be Too Long

Longer browser cache improves repeat visits, but makes updates harder to control.

For example, if HTML uses:

1
Cache-Control: public, max-age=31536000

After a user visits an article page once, the browser may consider that page fresh for a year. If you later fix a typo, update a link, or correct content, Cloudflare purge may not affect that user’s local browser cache.

Static assets can be cached longer because they usually have fingerprinted file names:

1
2
/scss/style.min.5e5f1f8f7ce389becd0a48fe3eff3e1c79b2750a903584200a3a6fc54ae1e664.css
/ts/main.9acdcb7a78e911f2c617b8cd4af2eab573c04169dddc51c052977dde63210ced.js

When the content changes, the filename changes too, so the old cache does not affect the new page’s reference.

HTML URLs usually stay the same:

1
/2026/06/13/browser-cache-vs-cloudflare-edge-cache/

So HTML is better served with a short browser TTL.

A Simple Configuration

If you want to keep things simple and avoid per-file-type rules, use one unified policy:

1
2
3
Cloudflare Edge TTL = 1 day
Browser TTL = 5 minutes
Every publish: purge everything

Cloudflare Cache Rule handles edge caching:

1
(http.host in {"knightli.com" "www.knightli.com"} and not starts_with(http.request.uri.path, "/cdn-cgi/"))

Action:

1
2
3
4
Eligible for cache
Edge TTL: 1 day
Browser TTL: 5 minutes
Query string: ignore

If origin Nginx sets longer Cache-Control for CSS, JS, or images, and you want every browser-facing response to be 5 minutes, add a Cloudflare Response Header Transform Rule:

1
2
3
4
5
Set:
Cache-Control: public, max-age=300

Remove:
Expires

This gives you:

  • Edge cache controlled by Cloudflare Cache Rule.
  • Browser cache controlled by Cloudflare response header rule.
  • Nginx does not need to be unified around browser TTL.

When Layered Caching Is Worth It

For higher performance, you can split by resource type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HTML:
  Edge TTL: 1 day
  Browser TTL: 5 minutes

CSS / JS / images / fonts:
  Edge TTL: 1 month
  Browser TTL: 1 month or 1 year

RSS / sitemap:
  Edge TTL: 1 hour
  Browser TTL: 5 minutes

This is more efficient, but also more complex to maintain. A personal blog does not need to start here.

Once you confirm all static assets have hashed filenames, you can safely increase the Browser TTL for CSS, JS, and images.

How to Verify It Works

Use curl -I to request the same URL twice:

1
2
curl.exe -I https://knightli.com/
curl.exe -I https://knightli.com/

Focus on two headers:

1
2
cf-cache-status: HIT
cache-control: public, max-age=300

cf-cache-status: HIT means Cloudflare edge cache was hit.

cache-control: public, max-age=300 means the browser-facing cache TTL is at most 300 seconds.

If you see:

1
cf-cache-status: MISS

It may be the first request, a recent purge, rules not fully propagated, or the resource not matching cache eligibility. A second request often becomes HIT.

If CSS or JS still shows:

1
cache-control: public, max-age=31536000, immutable

Then the browser cache header is still coming from the origin or a higher-priority rule. Either accept long cache for static assets, or override it with Cloudflare Response Header Transform Rule.

Summary

Browser cache and Cloudflare cache are not the same thing.

The practical rule is:

  • Cloudflare Edge cache can be aggressive because the site owner can purge it.
  • Browser cache should be conservative because the site owner cannot clear users’ local caches.
  • HTML pages should use a short Browser TTL.
  • Fingerprinted static assets can use a long Browser TTL.
  • For a static blog that purges everything on every deploy, Edge TTL of 1 day is a stable starting point.

For a Hugo static blog, starting with:

1
2
3
Cloudflare Edge TTL = 1 day
Browser TTL = 5 minutes
Every publish: purge everything

already captures most CDN acceleration benefits while avoiding old HTML pages sticking around in user browsers for too long.

References

记录并分享
Built with Hugo
Theme Stack designed by Jimmy