DEV Community

Usama Ejaz
Usama Ejaz

Posted on Originally published at justblogged.com

Edited a Blog Post but Readers Still See the Old Version? How to Find the Cache Holding It

You fix a typo in a published post, reload, and it's fixed. Then someone replies with a screenshot of the old version.

Something between the host and that reader is serving a stored copy. It's one of four places: the reader's browser, the host's CDN edge, a page cache on the host's servers, or a copy kept on some other service entirely (a social preview, a feed reader, Google). The first three show up in the post's response headers, so that's where I start.

On October 20, 2026 I pulled the headers from public blogs on nine platforms. Edge lifetimes went from 25 seconds on Hashnode to 12 hours on Bear. And the usual trick of adding a query string to bust the cache only worked on two of the four CDNs I tried it on.

One curl command

Run it against the public URL, from a terminal that isn't logged in to the blog:

curl -s -o /dev/null -D - https://yourblog.com/your-post/ \
  | grep -iE '^(cache-control|age|cf-cache-status|x-vercel-cache|cache-status|x-cache|last-modified):'
Enter fullscreen mode Exit fullscreen mode

What I look at:

  • cache-control: max-age=N lets a browser reuse its copy for N seconds without asking. s-maxage=N does the same for shared caches like a CDN, and RFC 9111 says it overrides max-age there.
  • no-cache or max-age=0 means a stored copy has to be revalidated before reuse. It does not mean "don't store it", which MDN's caching guide is clear about.
  • age is how many seconds the copy has been sitting in a shared cache. age: 5961 is an edge copy about 100 minutes old.
  • The cache-status header tells you if the edge answered from storage. The name depends on the CDN: cf-cache-status on Cloudflare, x-vercel-cache on Vercel, cache-status on Netlify, x-cache on Fastly. HIT means a stored copy.

What nine platforms sent

One or two requests per platform, to a real site on it. It's a snapshot... hosts change these, and individual sites add their own rules. Medium gave me a 403, so it's not here.

Platform Site checked Cache-Control on the post HTML Edge signal How long an old copy can live
GitHub Pages karpathy.github.io, jekyllrb.com max-age=600 x-cache via Fastly (Varnish) 10 minutes in a browser that already loaded it
Netlify docs.netlify.com public, max-age=0, must-revalidate cache-status: "Netlify Edge" Until the next deploy, which Netlify says clears the CDN cache
Vercel nextjs.org/blog public, max-age=0, must-revalidate x-vercel-cache: HIT, age: 7426 Until the next deployment, which gets its own cache key
Hashnode townhall.hashnode.com public, s-maxage=25 x-vercel-cache: MISS 25 seconds at the edge
Ghost(Pro) 404media.co public, max-age=0 x-cache: MISS, HIT, HIT, age: 8531 Browsers revalidate every time; the edge copy depends on Ghost(Pro) purging it
WordPress.com wordpress.com/blog max-age=137, must-revalidate (156 on an earlier request) x-nananana: Batcache-Hit About two and a half minutes in a browser
Substack on.substack.com no-cache cf-cache-status: HIT, age: 1470 Edge copy kept despite no-cache; lifetime not exposed
Bear herman.bearblog.dev public, s-maxage=43200, max-age=0 cf-cache-status: HIT, age: 5842 Up to 12 hours at the edge unless Bear purges on edit

I couldn't find current docs on when Ghost(Pro) or Bear purge the edge after an edit. The closest thing is a 2019 reply on the Ghost forum saying their custom X-Cache-Invalidate header is used on Ghost(Pro)'s caching layer. So the header test on your own post is the only reliable answer I have.

Step 1: rule out the browser

Only GitHub Pages and WordPress.com in that table give browsers a positive max-age. Inside that window, a browser that already has the page can show its stored copy when you click back to the post.

A normal reload sends Cache-Control: max-age=0 with the stored validators, so the browser checks with the server first. A force reload (Ctrl+Shift+R, Cmd+Shift+R on a Mac) sends no-cache and skips the stored copy. If either one shows the edit, it was the browser, and everyone else's browser catches up within the max-age. That's 10 minutes at most on GitHub Pages.

Step 2: check the edge copy's age

If a force reload still shows the old text, and the response has a HIT with an age longer than the time since your edit, the edge copy was stored before you saved. How long it stays is s-maxage or whatever rules the CDN has. On Bear that's 43,200 seconds.

Substack's row is the odd one. Cloudflare's docs say it doesn't cache HTML by default and skips responses marked no-cache. So a HIT on no-cache HTML means Substack set its own cache rule, with a lifetime the response doesn't tell you.

To confirm the edge is the problem, request the post with a throwaway query string like ?nocache=21404. The result depended on the CDN:

Platform Plain URL With a random query string
Bear HIT, age: 5961 MISS
Substack HIT, age: 1600 MISS
GitHub Pages HIT HIT
Ghost(Pro) HIT, age: 8531 HIT, age: 8651

On Bear and Substack the query string went to the origin. If that version has your edit and the plain URL doesn't, the edge copy is stale and gets replaced when it expires or the host purges it.

On GitHub Pages and Ghost(Pro) it still came back as a hit, and on Ghost(Pro) the age kept counting up. The cache was ignoring the query string. On those hosts this test can't tell the edge from the origin, which I think a lot of "just add ?v=2" advice misses.

If you put your own Cloudflare proxy in front of a hosted blog, you now have two edge caches with separate rules stacked on each other. Check both.

Step 3: check logged out

Page caches often treat signed-in visitors differently. The Batcache readme, which is the cache behind Batcache-Hit on WordPress.com, says it works "by serving old pages to new users," and that once visitors "have logged in or left a comment they will always get fresh pages."

So if you're signed in to your own blog, you see the edit and logged-out readers get the stored page. A private window or the curl above carries none of your cookies.

Step 4: the copies that aren't on your host

If the post URL serves the new version and someone still sees the old one, the copy lives on another service, and each one refreshes on its own schedule:

  • Link previews on social networks and chat apps keep the title, description and image they fetched first.
  • Feed readers only see the edit after they poll your feed again, which can be hours for a small blog.
  • Google's title and snippet come from its last crawl. Search Console's URL Inspection tool lets you run a live test and request indexing. Google says that "typically takes only a day or so" but can take longer, and requests have a daily limit.

I build JustBlogged, and when I ran the same curl on its post pages they came back with no Cache-Control, no age, no cache-status header and no Last-Modified. Nothing tells a browser or CDN to keep the page, so when someone there reports an old version it's always step 4. The flip side is every request goes to the origin, and I'm still not sure that's the right default

Top comments (0)