Introduction

When you update your site content but visitors still see the old version, the Cloudflare cache hasn't properly cleared. Cache purges can fail silently for several reasons: the purge URL pattern might be incomplete, cache rules might override TTL settings, origin headers might mark content as immutable, or another caching layer (browser, application) might be holding stale data. Systematic diagnosis identifies which cache layer is serving the outdated content.

Symptoms

  • Content updates not visible to visitors after purge
  • CSS/JS changes not reflecting in browser
  • HTML shows old content despite origin serving new version
  • Purge completes in dashboard but cache still serves old content
  • Some regions see new content while others see old
  • API responses return stale data after update

Common Causes

  • Purge URL pattern doesn't match all cached variants
  • Cache Rules override origin Cache-Control headers
  • Browser cache holding stale content independently
  • Cache-Control headers marking content as immutable or long-lived
  • Query string variations cached separately and not purged
  • CDN cache reserve tier still holding purged content
  • Multiple Cloudflare zones caching same content

Step-by-Step Fix

  1. 1.Verify origin server is serving updated content:

```bash # Test origin directly (bypass cache) curl -I http://YOUR_ORIGIN_IP/path -H "Host: yourdomain.com"

# Compare with Cloudflare-proxied response curl -I https://yourdomain.com/path ```

  1. 1.Check Cloudflare cache status headers:

```bash # Look for CF-Cache-Status header curl -I https://yourdomain.com/path | grep -E "CF-Cache-Status|cf-ray"

# CF-Cache-Status values: # HIT: served from cache # MISS: not in cache, fetched from origin # EXPIRED: was cached but TTL expired # STALE: serving stale while revalidating # BYPASS: cache skipped due to rules ```

  1. 1.Identify which URLs need purging:

```bash # Check all cached variations curl -I https://yourdomain.com/page curl -I https://yourdomain.com/page?param=1 curl -I https://yourdomain.com/page?param=2

# Each query string variation is cached separately ```

  1. 1.Perform complete cache purge:

Via Cloudflare Dashboard: - Go to Caching > Configuration - Click "Purge Everything" for full purge - Or use "Custom Purge" for specific URLs

Via API:

```bash # Purge everything 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 '{"purge_everything":true}'

# Purge specific URLs 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://yourdomain.com/page","https://yourdomain.com/style.css"]}' ```

  1. 1.Check Cache Rules that might override TTL:

Navigate to: Cloudflare Dashboard > Caching > Cache Rules

Look for rules that: - Set very long edge TTL - Override origin Cache-Control - Cache HTML pages indefinitely

bash
# Temporarily disable problematic rules for testing
# Or adjust TTL values to be shorter
  1. 1.Review origin Cache-Control headers:

```bash # Check what origin sends curl -I http://YOUR_ORIGIN_IP/path -H "Host: yourdomain.com" | grep -i cache

# Problematic headers: # Cache-Control: immutable - tells cache never to revalidate # Cache-Control: max-age=31536000 - 1 year cache # Cache-Control: public, max-age=86400 - 24 hour cache ```

Fix origin headers:

nginx
# nginx: Set appropriate cache headers
location /static/ {
    add_header Cache-Control "public, max-age=86400";
}
location / {
    add_header Cache-Control "no-cache, must-revalidate";
}
  1. 1.Clear browser cache to verify fix:

```bash # Test with fresh request (bypass browser cache) curl -H "Cache-Control: no-cache" https://yourdomain.com/path

# Or use browser dev tools # Network tab > Disable cache checkbox ```

  1. 1.Use cache tags for selective purging (if supported):
bash
# Purge by cache tag
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 '{"tags":["product-123","styles"]}'
  1. 1.Verify purge completed successfully:

```bash # After purge, check cache status curl -I https://yourdomain.com/path | grep CF-Cache-Status

# Should show MISS (fetched fresh from origin) ```

  1. 1.Implement cache versioning for assets:

```html <!-- Use versioned URLs for static assets --> <link rel="stylesheet" href="/style.css?v=2.0"> <script src="/app.js?v=2.0"></script>

<!-- Or use content hash --> <link rel="stylesheet" href="/style.a1b2c3.css"> ```

Verification

After applying fixes:

  1. 1.curl -I https://yourdomain.com/path shows CF-Cache-Status: MISS after purge
  2. 2.Origin content matches proxied content
  3. 3.Multiple regions show updated content
  4. 4.Browser with cleared cache sees new version
  5. 5.No stale content in subsequent requests