The most surprising thing about purging CDN cache is that you can’t truly "purge" it; you can only "invalidate" it, and the difference is critical for avoiding downtime.
Imagine a user requests https://example.com/image.jpg. Your CDN edge server might have a cached copy. When you want to update that image, you can’t just tell the edge server, "delete that file." Instead, you tell it, "this file is now stale; the next time someone asks for it, go back to the origin server and get the new version." This invalidation process takes time to propagate across the CDN’s global network. If you then immediately try to access https://example.com/image.jpg from a different edge server that hasn’t received the invalidation signal yet, you’ll still get the old image. This is the root of "stale content" issues.
To avoid downtime and serve the correct content immediately after an update, the standard practice is to combine cache invalidation with a versioned URL strategy.
Here’s how it works in practice. Let’s say your original URL is https://cdn.example.com/assets/styles.css.
-
Initial Deployment:
- You upload
styles.cssto your origin server. - Your web application serves it at
https://cdn.example.com/assets/styles.css. - The CDN caches this.
- You upload
-
Content Update:
- You modify
styles.cssand save it asstyles.v2.csson your origin server. - Crucially, you update your web application’s HTML to reference the new file:
<link rel="stylesheet" href="https://cdn.example.com/assets/styles.v2.css">. - You don’t invalidate the cache for
/assets/styles.css(which now points to the old file).
- You modify
-
New Users Get New Content:
- New users requesting
https://example.com/will get the updated HTML, which points tostyles.v2.css. - The CDN edge server for these new users will see
styles.v2.cssis not in its cache. It will fetchstyles.v2.cssfrom your origin and serve it. This is a cache miss, but it’s expected and results in the correct content.
- New users requesting
-
Old Content Eventually Expires:
- Users who previously had
https://cdn.example.com/assets/styles.csscached will continue to receive the old version until their cache TTL (Time To Live) expires. - Once the TTL for
styles.cssexpires on an edge server, that server will perform a cache miss on its next request forstyles.css, fetch the newstyles.cssfrom the origin (which is now the oldstyles.v2.css), and serve that. This is where the old URL eventually serves new content.
- Users who previously had
This versioning strategy ensures that any user making a new request after the deployment gets the correct, updated asset immediately, while existing users gradually receive the updated content as their cache TTLs expire. There’s no moment where a user might receive an inconsistent state.
Let’s look at a practical example using a common CDN provider’s API (syntax will vary, but the concept is identical).
Suppose you are using Cloudflare. You’ve deployed a new version of your main JavaScript file.
Original URL: https://cdn.example.com/app.js
New URL: https://cdn.example.com/app.1a2b3c4d.js
Your HTML would change from:
<script src="https://cdn.example.com/app.js"></script>
to:
<script src="https://cdn.example.com/app.1a2b3c4d.js"></script>
You would not typically run a cache purge command for /app.js immediately. Instead, you’d let its TTL expire naturally. If you needed to force older clients to get the new version faster, you’d use an invalidation API call, but the versioned URL is the primary mechanism for zero-downtime deployments.
Here’s how you might invalidate a specific file using Cloudflare’s API (after deploying the new version and updating your HTML):
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/<ZONE_ID>/purge" \
-H "X-Auth-Email: <EMAIL>" \
-H "X-Auth-Key: <API_KEY>" \
-H "Content-Type: application/json" \
--data '{"files": [{"url": "https://cdn.example.com/app.js", "recursive": false}]}'
This command tells Cloudflare to remove app.js from its cache. The recursive: false means it only purges that exact path. The recursive: true option would purge everything under that path, which is usually too aggressive and can lead to performance issues as the CDN has to refetch many assets.
The recursive: false flag is important. Using recursive: true on a path like /assets/ would tell the CDN to invalidate everything under /assets/, which could be hundreds or thousands of files. This forces the CDN to go back to your origin for every single one of those files, potentially overwhelming your origin server and making subsequent page loads much slower for users until the cache repopulates. The versioned URL strategy avoids this by only ever creating new cache entries and letting old ones expire.
The actual mechanism by which an edge server knows to fetch a new version when a URL changes is straightforward: it’s a cache miss. When a request arrives for app.1a2b3c4d.js and the edge server doesn’t have it, it checks its cache. If it’s not there, it’s a cache miss. The edge server then goes to the origin server to retrieve app.1a2b3c4d.js, caches it, and serves it to the user. This is the desired behavior. The complexity arises when you want to update content served at a static URL that many clients might have cached.
When you use the purge API with recursive: false for a specific file like app.js, you’re essentially forcing a cache miss for that exact URL. The next request for app.js will go to the origin. If you’ve already deployed the new version of your application to serve app.1a2b3c4d.js, this purge command is a way to clean up the old, now-unused app.js from the CDN’s edge, ensuring it doesn’t linger if a user’s browser somehow still requests it. It’s a cleanup step, not the primary deployment mechanism.
The core idea is that you are always adding new, versioned resources to your CDN and origin, and your application’s HTML/logic is updated to point to these new resources. The old resources are implicitly retired by not being referenced anymore. Cache invalidation is a tool to speed up the removal of old resources from the CDN, but it’s secondary to the versioning strategy for ensuring users get the correct content immediately.
The next challenge you’ll face is managing cache TTLs effectively for assets that don’t change frequently but might need occasional updates without re-versioning everything.