Introduction
CDN cache not purging means edge nodes continue serving stale content after you have deployed new assets. The origin server has the updated files, but visitors receive old HTML, CSS, JavaScript, or images because the CDN has not invalidated or refreshed its cached copies. This is a deployment integrity issue, not a DNS or origin problem.
Symptoms
- Deployed files show new timestamps on origin but old versions appear via CDN URL
- Some geographic regions show updated content while others serve stale data
- Hard refresh (Ctrl+F5) bypasses the issue, indicating browser cache is not the culprit
- Cache-control headers appear correct in origin responses but edge responses differ
- The problem affects specific file types or paths consistently after each deploy
Common Causes
- Cache invalidation API call failed silently or was rate-limited
- Cache keys include query strings or headers that prevent proper invalidation
- Origin sets
Cache-Control: privateorno-cacheinconsistently across resources - CDN TTL exceeds deployment frequency, and no explicit purge was triggered
- Wildcard invalidation patterns do not match the actual cached object paths
- Multiple CDN distributions exist, and only some were purged
Step-by-Step Fix
### 1. Verify origin has correct content
Access the origin directly (bypassing CDN) to confirm new files are deployed:
```bash # Compare origin vs CDN response curl -I https://origin.example.com/assets/app.js curl -I https://cdn.example.com/assets/app.js
# Check ETag or Last-Modified headers curl -I -H "Host: origin.example.com" https://cdn.example.com/assets/app.js ```
If origin shows stale content, the deploy pipeline did not complete successfully.
### 2. Check cache-control headers on origin
Ensure origin sends consistent, cacheable headers:
bash
curl -I https://origin.example.com/assets/app.js | grep -i cache-control
Expected: Cache-Control: public, max-age=31536000, immutable for versioned assets.
If headers show private, no-store, or very short max-age, the CDN may not cache predictably.
### 3. Trigger explicit cache invalidation
Use your CDN provider's API to purge affected paths:
```bash # Cloudflare example curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ --data '{"files":["https://cdn.example.com/assets/app.js","https://cdn.example.com/index.html"]}'
# AWS CloudFront example aws cloudfront create-invalidation \ --distribution-id DISTRIBUTION_ID \ --paths "/assets/app.js" "/index.html"
# Fastly example curl -X POST "https://api.fastly.com/service/SERVICE_ID/purge" \ -H "Fastly-Key: API_TOKEN" \ -H "Accept: application/json" ```
Note: Wildcard purges (/*) may have rate limits or delayed propagation.
### 4. Verify cache key configuration
CDN cache keys often include query strings, headers, or cookies. If your invalidation does not match the cached key, purge fails silently:
- Check if CDN caches
?v=123and?v=456as separate objects - Verify
Varyheaders (Accept-Encoding, User-Agent) do not multiply cache entries - Confirm host headers match if CDN serves multiple domains
### 5. Wait for edge propagation
After purge, edge nodes may take 30 seconds to 5 minutes to reflect changes globally. Test from multiple locations:
bash
# Use dnschecker or similar to test from different regions
curl -x us-east.cdn-provider.net:80 -I https://cdn.example.com/assets/app.js
curl -x eu-west.cdn-provider.net:80 -I https://cdn.example.com/assets/app.js
### 6. Implement versioned asset URLs
Prevent cache conflicts by fingerprinting assets:
```html <!-- WRONG: Static filename invites cache issues --> <script src="/assets/app.js"></script>
<!-- CORRECT: Hash-based filename forces fresh fetch --> <script src="/assets/app.a1b2c3d4.js"></script> ```
Build tools like Webpack, Vite, or Rollup can generate hashed filenames automatically.
### 7. Add deploy pipeline cache purge step
Automate invalidation in CI/CD:
yaml
# GitHub Actions example
- name: Purge CDN cache
run: |
curl -X POST "https://api.cdn-provider.com/purge" \
-H "Authorization: ${{ secrets.CDN_API_KEY }}" \
-d "paths=/assets/*,/index.html"
### 8. Monitor cache hit ratios
After resolution, track cache behavior:
- High hit ratio on stale paths indicates purge did not propagate
- Sudden drop to origin suggests over-aggressive invalidation
- Set up alerts for cache miss spikes after deploys
Prevention
- Version all static assets with content hashes
- Configure CDN to respect origin cache headers
- Set reasonable TTLs (1 year for versioned, 5 min for HTML)
- Automate cache purge as part of deploy pipeline
- Test cache invalidation in staging environment before production