Introduction
Content Delivery Networks (CDNs) cache website content at edge locations to improve load times. When content is updated on the origin server but the CDN cache is not purged, visitors continue to see stale cached content. This is a common issue after website updates, CMS content changes, or CSS/JavaScript deployments.
Symptoms
- Updated website content is not visible to visitors
- Direct access to the origin server shows new content, but CDN URL shows old content
- CDN cache headers show
HITfor stale content - CDN purge API returns success but cache is not actually cleared
- Error message:
X-Cache: HITfor content that should have been updated
Common Causes
- CDN purge API call failing silently due to authentication error
- Cache purge not triggered automatically after content update
- CDN cache TTL set too long (e.g., 24 hours or more)
- CDN configuration not matching the correct origin URL
- Purge request not propagating to all edge locations
Step-by-Step Fix
- 1.Verify the CDN cache is serving stale content: Check cache headers.
- 2.```bash
- 3.curl -I https://cdn.example.com/image.jpg
- 4.# Look for:
- 5.# X-Cache: HIT (served from cache)
- 6.# Age: 86400 (cached for 24 hours)
- 7.
` - 8.Manually purge the CDN cache: Clear all cached content.
- 9.```bash
- 10.# Cloudflare example
- 11.curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
- 12.-H "Authorization: Bearer $API_TOKEN" \
- 13.-H "Content-Type: application/json" \
- 14.-d '{"purge_everything": true}'
# CloudFront example aws cloudfront create-invalidation \ --distribution-id $DISTRIBUTION_ID \ --paths "/*" ```
- 1.Verify the cache is purged: Check that the origin is being hit.
- 2.```bash
- 3.curl -I https://cdn.example.com/image.jpg
- 4.# Should show:
- 5.# X-Cache: MISS (fetched from origin)
- 6.# Age: 0
- 7.
` - 8.Configure automatic cache purging on content update: Set up webhooks or plugins.
- 9.```php
- 10.// WordPress: use a CDN purge plugin
- 11.// Or add to functions.php
- 12.add_action('save_post', function($post_id) {
- 13.// Trigger CDN purge via API
- 14.wp_remote_post('https://api.cdn-provider.com/purge', [
- 15.'headers' => ['Authorization' => 'Bearer ' . CDN_API_KEY],
- 16.'body' => json_encode(['urls' => [get_permalink($post_id)]])
- 17.]);
- 18.});
- 19.
` - 20.Set appropriate cache TTL for different content types: Balance freshness and performance.
- 21.```nginx
- 22.# Origin server cache headers
- 23.location ~* \.(css|js)$ {
- 24.expires 1h;
- 25.add_header Cache-Control "public, max-age=3600";
- 26.}
- 27.location ~* \.(jpg|png|gif)$ {
- 28.expires 30d;
- 29.add_header Cache-Control "public, max-age=2592000";
- 30.}
- 31.
`
Prevention
- Configure CDN to respect origin Cache-Control headers
- Set up automatic cache purging via webhooks or CMS plugins
- Use cache-busting query strings or versioned filenames for CSS/JS files
- Monitor CDN cache hit rates and purge success rates
- Test content visibility through the CDN after every update
- Implement staging environments to verify content before CDN cache is updated