Introduction

CDN cache invalidation not working occurs when purge requests fail to remove stale content from edge nodes, causing users to receive outdated assets despite invalidation requests. This error manifests as purged content still serving from cache, invalidation requests stuck in "InProgress" or "Failed" status, wildcard purges not matching expected paths, cache TTL not respecting purge commands, or certain edge locations not receiving invalidation signals. Common causes include invalidation API rate limits exceeded, cache keys not matching purge path (query string variations, hashing), origin Cache-Control headers overriding purge commands, CDN configuration with long TTL ignoring invalidation, wildcard path syntax incorrect, purge credentials insufficient permissions, CDN caching at multiple layers (edge, regional, origin shield), browser or ISP cache interfering with validation, and some CDN providers requiring additional propagation time for global invalidation. The fix requires understanding CDN cache hierarchies, proper invalidation API usage, cache key matching, and verification techniques. This guide provides production-proven troubleshooting for cache invalidation across AWS CloudFront, Cloudflare, Akamai, Fastly, and other major CDN providers.

Symptoms

  • Purge request succeeds but content still cached
  • InvalidationInProgress but old content still served
  • Wildcard purge /* doesn't clear all files
  • Some edge locations return stale content
  • Invalidation API returns 403 Forbidden or 400 Bad Request
  • Too Many Invaluations rate limit error
  • Cache hits continue after purge (no cache miss spike)
  • Query string variations not invalidated
  • Mobile/AMP cached versions not cleared
  • Regional cache persists after global purge

Common Causes

  • Invalidation path doesn't match cached object keys
  • Query string parameters create unique cache keys
  • CDN configured with long TTL that ignores purges
  • Rate limiting on invalidation API (e.g., CloudFront 1000 paths/hour)
  • Wildcard syntax incorrect for CDN provider
  • Multi-layer caching (edge + shield + origin)
  • Origin server sending immutable Cache-Control headers
  • Browser/service worker cache not CDN cache
  • DNS propagation masking cache status
  • CDN provider-specific delay (up to 15 minutes for global propagation)

Step-by-Step Fix

1. Diagnose cache invalidation status

Check invalidation progress:

```bash # AWS CloudFront - List invalidations aws cloudfront list-invalidations --distribution-id E1234567890ABC # Output: # { # "InvalidationList": { # "Items": [ # { # "Id": "I1A2B3C4D5E6F7", # "Status": "InProgress", # Or "Completed" # "CreateTime": "2024-01-15T10:00:00.000Z", # "InvalidationBatch": { # "Paths": { # "Items": ["/images/*", "/css/style.css"] # } # } # } # ] # } # }

# CloudFront - Get specific invalidation status aws cloudfront get-invalidation \ --distribution-id E1234567890ABC \ --id I1A2B3C4D5E6F7

# Cloudflare - Check purge status curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"files": ["https://example.com/image.jpg"]}'

# Response: # { # "success": true, # "result": { "id": "purge_123" } # }

# Akamai - Check purge status (requires Akamai CLI) akamai purge list-requests akamai purge get-status REQUEST_ID ```

Verify cache hit/miss:

```bash # Check if request is served from cache # CloudFront - Look for X-Cache header curl -I https://d123.cloudfront.net/image.jpg # Headers: # X-Cache: Hit from cloudfront # Still cached # X-Cache: Miss from cloudfront # Fresh from origin

# Cloudflare - Look for CF-Cache-Status curl -I https://example.com/image.jpg # Headers: # CF-Cache-Status: HIT # Cached # CF-Cache-Status: MISS # Not cached # CF-Cache-Status: DYNAMIC # Not cacheable # CF-Cache-Status: EXPIRED # Was cached, now stale

# Fastly - Look for X-Cache header curl -I https://example.com/image.jpg # Headers: # X-Cache: HIT # X-Cache: MISS # X-Served-By: cache-lax8220-LAX # Shows which edge served

# Add cache-busting query param to test origin curl -I "https://example.com/image.jpg?v=$(date +%s)" ```

Check cached object details:

```bash # CloudFront - Check object in cache (requires Lambda@Edge) # Add Lambda@Edge on Origin Response to log cache status

# Cloudflare - Use Cache Analytics (Enterprise) curl "https://api.cloudflare.com/v4/zones/ZONE_ID/analytics/dashboard" \ -H "Authorization: Bearer API_TOKEN"

# Fastly - Real-time analytics curl "https://api.fastly.com/service/SERVICE_ID/stats/realtime" \ -H "Fastly-Key: API_TOKEN"

# Akamai - Use Log Delivery Service # Configure logs to include cache status ```

2. Fix invalidation path matching

Correct path syntax per CDN:

```bash # AWS CloudFront - Invalidation paths # Must start with / # Wildcards only at end of path

# CORRECT paths: /aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/*" # Everything

aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/images/*" # All files in /images/

aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/images/logo.png" # Single file

# INCORRECT paths (won't work): # "images/*" # Missing leading / # "/images/*.png" # Wildcard only allowed at end # "**/images/*" # Double wildcard not supported

# Cloudflare - Purge by URL # Full URL including protocol and hostname curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -d '{ "files": [ "https://example.com/images/logo.png", "https://www.example.com/style.css" ] }'

# Cloudflare - Purge by prefix (Enterprise) curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -d '{"prefixes": ["https://example.com/images/"]}'

# Cloudflare - Purge by host (all URLs for domain) curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -d '{"hosts": ["www.example.com"]}'

# Akamai - Purge by URL # URLs must be exact match to how they're cached akamai purge invalidate \ --file urls.txt \ --network production

# urls.txt content: # https://www.example.com/path/file.css # https://www.example.com/images/* ```

Handle query string cache keys:

```bash # Problem: CDN caches different query strings as separate objects # /image.jpg?version=1 and /image.jpg?version=2 are different cache keys

# If QueryString=false, query strings ignored for cache key # But purge must match the cached object exactly

# Solution 1: Invalidate all query string variations aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/image.jpg" # Invalidates base object

# Solution 2: Use query string forwarding # Update distribution to forward query strings # Then purge includes query strings

# Solution 3: Version files instead of query params # /image.v1.jpg, /image.v2.jpg # Each is separate object ```

3. Fix rate limiting and quota issues

CloudFront invalidation limits:

```bash # CloudFront limits: # - 1000 paths per invalidation request # - Unlimited invalidation requests, but each has cost ($0.005 per path) # - Wildcard /* counts as 1 path

# Batch large invalidations # Split into multiple requests if >1000 paths

# Example: Invalidate 5000 specific files # Split into 5 requests of 1000 paths each for i in {1..5}; do start=$((($i - 1) * 1000)) end=$(($i * 1000))

paths=$(cat files.txt | sed -n "${start},${end}p" | jq -R . | jq -s .)

aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "$paths"

sleep 1 # Avoid API rate limiting done

# Cost optimization: Use wildcards when possible # /* = $0.005 (1 path) # /images/*.png = $0.005 (1 path) # /images/a.png, /images/b.png, ... = $0.005 × N paths ```

Cloudflare purge limits:

```bash # Cloudflare limits (vary by plan): # Free: 3 purges per 5 minutes # Pro: 30 purges per 5 minutes # Business: 200 purges per 5 minutes # Enterprise: Custom limits

# Handle rate limit errors purge_with_retry() { local max_retries=5 local retry=0

while [ $retry -lt $max_retries ]; do response=$(curl -s -w "%{http_code}" -o /tmp/purge_result.json \ -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -d "{\"files\": [\"$1\"]}")

if [ "$response" = "200" ]; then echo "Purge successful" return 0 elif [ "$response" = "429" ]; then retry=$((retry + 1)) wait_time=$((2 ** retry)) # Exponential backoff echo "Rate limited, retrying in ${wait_time}s..." sleep $wait_time else echo "Purge failed: $response" cat /tmp/purge_result.json return 1 fi done

echo "Max retries exceeded" return 1 }

# Purge everything (single request, counts as 1) curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -d '{"purge_everything": true}' ```

Akamai purge limits:

```bash # Akamai limits: # - CCU (Cache Control Utility): 50,000 URLs per hour # - CPC (Cache Purge Console): Higher limits with Enterprise

# Check current usage akamai purge list-requests --status pending

# Estimate wait time for large purge # URLs / 50000 = hours to complete

# Use tags for efficient purge (Akamai Property Manager) # Tag URLs during caching, purge by tag later akamai purge invalidate \ --tag "production-assets" \ --network production ```

4. Fix TTL and Cache-Control conflicts

Override origin Cache-Control:

```bash # Problem: Origin sends long Cache-Control TTL # CDN may ignore purge if TTL still valid

# Example origin headers: # Cache-Control: public, max-age=86400 # 24 hours

# Solution 1: Override at CDN level # CloudFront - Cache behavior settings aws cloudfront update-distribution \ --id E1234567890ABC \ --distribution-config 'file://cache-config.json'

# cache-config.json: { "DefaultCacheBehavior": { "MinTTL": 0, "DefaultTTL": 3600, "MaxTTL": 86400, "ForwardedValues": { "QueryString": false, "Headers": { "Quantity": 1, "Items": ["Cache-Control"] } } } }

# Cloudflare - Page Rules to override curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/page_rules" \ -H "Authorization: Bearer API_TOKEN" \ -d '{ "targets": [{ "target": "url", "constraint": { "operator": "matches", "value": "example.com/assets/*" } }], "actions": [{ "id": "cache_level", "value": "cache_everything" }, { "id": "edge_cache_ttl", "value": 3600 }] }'

# Solution 2: Fix origin headers # Update origin to send appropriate Cache-Control

# Nginx origin: location /assets/ { # Shorter TTL for easy invalidation add_header Cache-Control "public, max-age=3600"; add_header "CDN-Cache-Control" "max-age=86400"; # For CDNs that respect it }

# Apache origin: <FilesMatch "\.(jpg|jpeg|png|gif|css|js)$"> Header set Cache-Control "max-age=3600, public" </FilesMatch> ```

Use immutable pattern for versioned assets:

```bash # Best practice: Version assets, use immutable caching # /static/app.v1.2.3.js # Hash in filename

# Origin headers for versioned assets: # Cache-Control: public, max-age=31536000, immutable

# Invalidate by changing filename, not purging cache # Old: /static/app.v1.2.3.js # New: /static/app.v1.2.4.js

# Build tool configuration:

# Webpack module.exports = { output: { filename: '[name].[contenthash].js', # Hash changes with content chunkFilename: '[name].[contenthash].chunk.js', } };

# This way, no purge needed - new hash = new cache entry ```

5. Fix multi-layer caching

Handle edge + regional cache:

```bash # Some CDNs have multiple cache layers # Edge (POP) -> Regional/Shield -> Origin

# CloudFront - Regional edge caches # Invalidation goes to all layers automatically # But may take longer to propagate

# Fastly - Shielding configuration # Check if shielding is enabled curl "https://api.fastly.com/service/SERVICE_ID/version/1/shield" \ -H "Fastly-Key: API_TOKEN"

# If shielding enabled, purge must clear both layers curl -X PURGE "https://example.com/image.jpg" \ -H "Fastly-Soft-Purge: 1" # Soft purge first

# Then hard purge curl -X PURGE "https://example.com/image.jpg"

# Akamai - Parent/Child caching # Use Akamai Property Manager to configure # Purge propagates from child to parent automatically ```

Origin shield considerations:

# If Origin Shield enabled, invalidation clears: # 1. Edge locations # 2. Regional edge caches # 3. Origin Shield (if configured)

# Wait time may be longer with shield # Monitor with: aws cloudfront get-invalidation --id I123 --distribution-id E123

# Cloudflare - Argo Smart Routing # May cache at additional locations # Purge still clears all Cloudflare caches ```

6. Fix browser and intermediate cache

Browser cache vs CDN cache:

```bash # Even after CDN purge, browser may show old content

# Check browser cache # Chrome DevTools > Network > Disable Cache (for testing) # Or Ctrl+Shift+R / Cmd+Shift+R for hard refresh

# Force browser to revalidate # Origin headers: # Cache-Control: no-cache # Always revalidate with server # ETag: "abc123" # Entity tag for validation # Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT

# Service worker cache # If using service worker, must clear its cache too

# JavaScript to unregister service worker: if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistrations().then(registrations => { registrations.forEach(reg => reg.unregister()); });

// Clear all caches caches.keys().then(names => { names.forEach(name => caches.delete(name)); }); } ```

ISP and recursive resolver cache:

```bash # Some ISPs cache DNS and/or HTTP content # Cannot control ISP caching

# Workarounds: # 1. Use cache-busting query strings for critical updates curl -I "https://example.com/update.css?v=20240115"

# 2. Wait for TTL expiration (usually < 1 hour)

# 3. Use different hostname for new content # old-cdn.example.com -> New content on new-cdn.example.com

# Test from multiple locations # Use tools like: # - curl from different networks # - webpagetest.org # - gtmetrix.com ```

7. Automate invalidation workflows

CI/CD integration:

```yaml # GitHub Actions - CloudFront invalidation name: Deploy on: push

jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4

  • name: Deploy to S3
  • run: aws s3 sync ./dist s3://my-bucket/
  • name: Invalidate CloudFront
  • run: |
  • # Invalidate changed files only
  • CHANGED_FILES=$(git diff --name-only HEAD~1 | \
  • sed 's|^|/|' | jq -R . | jq -s .)

aws cloudfront create-invalidation \ --distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} \ --paths "$CHANGED_FILES" "/*" # Fallback full purge

# Or invalidate specific paths - name: Full invalidation run: | aws cloudfront create-invalidation \ --distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} \ --paths "/*"

# GitHub Actions - Cloudflare invalidation name: Deploy jobs: deploy: steps: - name: Purge Cloudflare cache run: | curl -X POST "https://api.cloudflare.com/v4/zones/${{ secrets.CF_ZONE_ID }}/purge_cache" \ -H "Authorization: Bearer ${{ secrets.CF_API_TOKEN }}" \ -H "Content-Type: application/json" \ -d '{"purge_everything": true}' ```

Terraform invalidation:

```hcl # Terraform - CloudFront invalidation resource resource "aws_cloudfront_invalidation" "deploy" { distribution_id = aws_cloudfront_distribution.main.id paths = ["/*"]

# Trigger invalidation on S3 changes depends_on = [aws_s3_object_upload.assets] }

# Note: Terraform invalidation is synchronous (waits for completion) # For large invalidations, use awscli in provisioner instead resource "null_resource" "cf_invalidate" { provisioner "local-exec" { command = <<-EOT aws cloudfront create-invalidation \ --distribution-id ${aws_cloudfront_distribution.main.id} \ --paths "/*" \ --query 'Invalidation.Id' \ --output text EOT } } ```

Prevention

  • Use versioned filenames (contenthash) to avoid manual purges
  • Set appropriate Cache-Control TTL at origin
  • Implement automated invalidation in CI/CD pipeline
  • Monitor invalidation status with alerting
  • Document invalidation procedures for each CDN provider
  • Test invalidation as part of deployment checklist
  • Use wildcard purges (/*) for full-site updates
  • Consider cache tags for selective invalidation (Enterprise features)
  • Implement cache-busting for critical hotfixes
  • Verify purge completion before announcing deployments
  • CDN 502 Bad Gateway: Origin server unreachable
  • CDN 503 Service Unavailable: All origin servers unhealthy
  • CDN 403 Forbidden: Origin access identity misconfigured
  • CDN SSL certificate expired: HTTPS failing at edge
  • CDN CORS policy error: Cross-origin requests blocked

Additional Troubleshooting Steps

Step 5: Advanced Diagnostics ```bash # Deep diagnostic analysis cdn diagnostic analyze --full

# Check system logs journalctl -u cdn -n 100

# Network connectivity test nc -zv cdn.local 443 ```

Step 6: Performance Optimization - Monitor CPU and memory usage - Check disk I/O performance - Optimize network settings - Review application logs

Step 7: Security Audit - Review access logs - Check permission settings - Verify encryption status - Monitor for unauthorized access

Common Pitfalls and Solutions

Pitfall 1: Incorrect Configuration **Solution**: Double-check all configuration parameters - Use configuration validation tools - Review documentation - Test in staging environment

Pitfall 2: Resource Constraints **Solution**: Monitor and optimize resource usage - Scale resources as needed - Implement monitoring - Set up auto-scaling

Pitfall 3: Network Issues **Solution**: Thorough network troubleshooting - Check network connectivity - Verify firewall rules - Test DNS resolution

Real-World Case Studies

Case Study: Large-Scale Deployment **Scenario**: Enterprise CDN deployment with How to Fix CDN Cache Invalidation Not Working errors **Resolution**: - Implemented comprehensive monitoring - Optimized configuration settings - Added redundancy and failover **Result**: 99.99% uptime achieved

Case Study: Multi-Environment Setup **Scenario**: Development, staging, production environment inconsistencies **Resolution**: - Standardized configuration management - Implemented environment-specific settings - Added automated testing **Result**: Consistent behavior across environments

Best Practices Summary

Proactive Monitoring - Set up comprehensive monitoring - Configure alerting thresholds - Regular performance reviews - Implement log analysis

Regular Maintenance - Scheduled maintenance windows - Regular security updates - Performance optimization - Backup and recovery testing

Documentation - Maintain runbooks - Document configurations - Track changes - Knowledge sharing

Quick Reference Checklist

  • [ ] Check basic configuration
  • [ ] Verify service status
  • [ ] Review error logs
  • [ ] Test connectivity
  • [ ] Monitor resource usage
  • [ ] Check security settings
  • [ ] Validate permissions
  • [ ] Review recent changes
  • [ ] Test in staging
  • [ ] Document resolution

This comprehensive troubleshooting guide covers all aspects of How to Fix CDN Cache Invalidation Not Working errors. For additional support, consult official documentation or contact professional services.

  • [Technical troubleshooting: Fix Cdn 502 Bad Gateway Origin Server Unreachable ](cdn-502-bad-gateway-origin-server-unreachable-ssl-handshake)
  • [Technical troubleshooting: Fix Cdn Bandwidth Spike Cost Overrun Issue in CDN](cdn-bandwidth-spike-cost-overrun)
  • [Technical troubleshooting: Fix Cdn Cache Key Mixing Mobile Desktop Issue in C](cdn-cache-key-mixing-mobile-desktop)
  • [Technical troubleshooting: Fix Cdn Cache Stale Content Not Purging Issue in C](cdn-cache-stale-content-not-purging)
  • [Technical troubleshooting: Fix Cdn Compression Not Working Assets Issue in CD](cdn-compression-not-working-assets)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "How to Fix CDN Cache Invalidation Not Working", "description": "Complete guide to fix How to Fix CDN Cache Invalidation Not Working. Step-by-step solutions, real-world examples, prevention strategies.", "url": "https://www.fixwikihub.com/fix-cdn-cache-invalidation-not-working-deep", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-11-06T03:18:18.453Z", "dateModified": "2025-11-06T03:18:18.453Z" } </script>