Introduction

CDN origin shield cache hierarchy errors occur when multi-layer caching configurations cause stale content delivery, cache key collisions, purge failures, or origin overload despite having shield/layer caching enabled. Origin shield (also called mid-tier cache, shield caching, or hierarchical caching) adds an intermediate cache layer between edge nodes and the origin server, reducing origin requests by having edge nodes fetch from the shield instead of directly from origin. Common causes include cache key mismatch between edge and shield layers, TTL configuration conflicts (edge TTL longer than shield TTL), purge not propagating through cache hierarchy correctly, cache stampede when shield cache expires simultaneously across edge nodes, inconsistent hash keys causing different edge nodes to fetch different shield content, origin requests bypassing shield due to cookie/query string misconfiguration, shield cache region selection suboptimal for traffic distribution, purge API rate limiting causing incomplete invalidation, and cache key normalization differences between edge and shield. The fix requires understanding cache hierarchy flow, aligning TTL configurations, ensuring consistent cache key generation, and implementing proper purge propagation. This guide provides production-proven troubleshooting for origin shield errors across CloudFront, Cloudflare, Fastly, and Akamai CDN deployments.

Symptoms

  • Origin server receives more requests than expected with shield enabled
  • Stale content served after purge from some edge locations
  • Cache hit ratio lower than expected with shield configured
  • Different edge nodes serve different content versions simultaneously
  • Purge takes longer than expected to propagate globally
  • Origin overload during cache expiration (thundering herd)
  • Cache key collisions causing wrong content served
  • Shield cache miss rate increasing over time
  • Inconsistent cache headers between edge and shield responses
  • Origin requests bypassing shield for cacheable content

Common Causes

  • Cache key includes dynamic elements (cookies, session IDs, timestamps)
  • Edge TTL exceeds shield TTL causing shield cache misses
  • Purge API only invalidates edge, not shield layer
  • Cache key normalization differs between edge and shield
  • Query string parameters not consistently forwarded to shield
  • Shield region selection doesn't match traffic patterns
  • Origin Connect-Time header not preserved through shield
  • Cache bypass rules too broad (cookies, authentication)
  • Shield cache size insufficient for content catalog
  • TTL not being refreshed on shield cache hits

Step-by-Step Fix

### 1. Diagnose cache hierarchy configuration

Check current CDN cache configuration:

```bash # CloudFront - Check cache behaviors aws cloudfront get-distribution-config --id E1234567890ABC \ --query 'DistributionConfig.DefaultCacheBehavior'

# Look for: # - MinTTL, MaxTTL, DefaultTTL values # - ForwardedValues configuration # - Compress settings # - ViewerProtocolPolicy

# CloudFront - Check origin shield configuration aws cloudfront get-distribution-config --id E1234567890ABC \ --query 'DistributionConfig.Origins.Items[*].OriginShield'

# Expected output: # { # "Enabled": true, # "OriginShieldRegion": "us-east-1" # }

# Cloudflare - Check cache rules via API curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/rules/cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json"

# Cloudflare - Check cache hit ratio curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/analytics/dashboard" \ -H "Authorization: Bearer API_TOKEN" \ -G --data-urlencode "since=-7d"

# Fastly - Check cache configuration via API curl -X GET "https://api.fastly.com/service/SERVICE_ID/version/VERSION/clustering" \ -H "Fastly-Key: FASTLY_API_TOKEN"

# Fastly - Check current cache stats curl -X GET "https://api.fastly.com/service/SERVICE_ID/stats" \ -H "Fastly-Key: FASTLY_API_TOKEN" \ -G --data-urlencode "from=-1h" ```

Analyze cache key generation:

```bash # Test cache key consistency across requests # Send identical requests and compare cache keys

# CloudFront - Check cache key via headers curl -vI https://d1234567890abcdef.cloudfront.net/object.key \ | grep -E "X-Cache|Age|Via|X-Amz-Cf"

# X-Cache: Hit from cloudfront (edge hit) # X-Cache: Miss from cloudfront (edge miss, check shield) # Via: 2.0 xxx.cloudfront.net (CloudFront) # Age: 3600 (seconds since cached)

# Cloudflare - Check cache key via headers curl -vI https://example.com/object.key \ | grep -E "CF-Cache-Status|CF-Ray|Age|cf-cache-status"

# CF-Cache-Status: HIT (edge hit) # CF-Cache-Status: MISS (edge miss, may be shield hit) # CF-Cache-Status: EXPIRED (TTL expired) # CF-Cache-Status: BYPASS (cache bypassed)

# Fastly - Check cache key via headers curl -vI https://example.com/object.key \ | grep -E "X-Cache|X-Served-By|X-Timer|Age"

# X-Cache: HIT (varnish hit) # X-Cache: MISS (backend fetch) # X-Served-By: cache-xyz123 (edge server ID) # X-Timer: S1234567890.V123,ve

# Akamai - Check cache key via headers curl -vI https://example.com/object.key \ | grep -E "X-Cache|X-Check-Cacheable|Cache-Key"

# X-Cache: TCP Hit from a123-45-67-8.deploy.akamaitechnologies.com # X-Check-Cacheable: true ```

Check origin request patterns:

```bash # Monitor origin request volume with shield enabled # Compare expected vs actual origin requests

# CloudFront - Origin request metrics aws cloudwatch get-metric-statistics \ --namespace AWS/CloudFront \ --metric-name Requests \ --Dimensions Name=Distribution,Value=E1234567890ABC \ Name=Region,Value=Global \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 300 \ --statistics Sum

# CloudFront - Origin-specific metrics aws cloudwatch get-metric-statistics \ --namespace AWS/CloudFront \ --metric-name 5xxErrorRate \ --Dimensions Name=Distribution,Value=E1234567890ABC \ Name=Origin,Value=origin.example.com \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 300 \ --statistics Average

# Check origin logs for cache bypass patterns # Look for requests that should have been cached awk '$9 == 200 && $10 > 1000 {print $0}' /var/log/nginx/access.log | \ grep -v "Cache-Control: no-cache" | head -20

# Identify cache bypass causes # - Cookies present # - Query strings varying # - Authorization headers # - Cache-Control: no-cache from client ```

### 2. Fix cache key configuration

CloudFront cache key policy:

```yaml # CloudFront - Cache key configuration # Update distribution cache behavior

aws cloudfront update-distribution --id E1234567890ABC --if-match ETAG --distribution-config ' { "DefaultCacheBehavior": { "TargetOriginId": "origin-s3", "ViewerProtocolPolicy": "https-only", "MinTTL": 0, "DefaultTTL": 3600, "MaxTTL": 86400,

"CachePolicyId": "cache-policy-id",

"OriginRequestPolicyId": "origin-request-policy-id" },

"CachePolicies": { "Items": [{ "CachePolicy": { "Id": "cache-policy-id", "Name": "CustomCachePolicy", "ParametersConfig": { "CookiesConfig": { "CookieBehavior": "none" # Exclude all cookies from cache key }, "HeadersConfig": { "HeaderBehavior": "whitelist", "Headers": { "Quantity": 1, "Items": ["Accept"] } }, "QueryStringsConfig": { "QueryStringBehavior": "whitelist", # Only specific query params "QueryStrings": { "Quantity": 2, "Items": ["version", "format"] } } } } }] } }'

# Cache key best practices: # - Exclude session cookies (JSESSIONID, PHPSESSID) # - Exclude tracking cookies (_ga, _fbp) # - Include version parameter if used for cache busting # - Include format parameter for content negotiation # - Exclude timestamp/random parameters ```

Cloudflare cache rules:

```bash # Cloudflare - Create cache rule to standardize cache key curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/rules/cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "description": "Standardize cache key for static assets", "priority": 1, "action": "cache", "action_parameters": { "cache_key": { "exclude_query": ["utm_*", "fbclid", "gclid"], "include_query": ["version", "format", "v"], "exclude_cookies": ["*"], "custom_key": { "header": { "include": ["accept"], "check_presence": ["accept-encoding"] } } }, "edge_ttl": 3600, "browser_ttl": 300 }, "expression": "path matches \"^/static/.*\"" }'

# Cloudflare - Page rules for legacy cache configuration curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/pagerules" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "targets": [{ "target": "url", "constraint": { "operator": "matches", "value": "example.com/static/*" } }], "actions": [ { "id": "cache_level", "value": "cache_everything" }, { "id": "browser_cache_ttl", "value": 300 }, { "id": "edge_cache_ttl", "value": 3600 } ], "status": "active", "priority": 1 }'

# Query string rules: # - Exclude: utm_*, fbclid, gclid (tracking params) # - Exclude: _t, timestamp, nonce (cache busters) # - Include: version, v, format (content variants) ```

Fastly cache key configuration:

```vcl # Fastly VCL - Customize cache key in Compute@Edge or VCL

# In VCL (via Fastly UI or API) sub vcl_hash { # Default hash includes host and URL # Customize to exclude problematic elements

# Remove query strings that should not affect cache if (req.url ~ "\?(.*)(utm_|fbclid|gclid|_t|timestamp|nonce)") { set req.url = regsub(req.url, "\?.*", "") }

# Keep only specific query parameters if (req.url ~ "\?") { set req.http.X-Query-Clean = ""

# Parse and filter query string # version, format, v are kept if (req.url ~ "version=") { set req.http.X-Query-Clean = "?version=" + querystring.get(req.url, "version"); } if (req.url ~ "format=") { if (req.http.X-Query-Clean != "") { set req.http.X-Query-Clean = req.http.X-Query-Clean + "&format=" + querystring.get(req.url, "format"); } else { set req.http.X-Query-Clean = "?format=" + querystring.get(req.url, "format"); } }

# Use cleaned query string in hash if (req.http.X-Query-Clean != "") { hash_data(req.http.X-Query-Clean) } }

# Exclude cookies from cache key (they are in req.http.Cookie but not hashed) # Only hash elements that should affect cache

hash_data(req.url)

return (hash) }

# Set appropriate TTLs sub vcl_fetch { # For static assets if (req.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$") { set beresp.ttl = 1h; set beresp.http.Cache-Control = "public, max-age=3600"; }

# For API responses if (req.url ~ "^/api/") { set beresp.ttl = 5m; set beresp.http.Cache-Control = "public, max-age=300"; }

return (deliver) } ```

### 3. Fix TTL hierarchy

Configure TTL layers correctly:

```yaml # TTL Hierarchy Rules: # 1. Origin Cache-Control headers are source of truth # 2. Shield TTL should be >= Edge TTL # 3. Browser TTL should be <= Edge TTL # 4. Use stale-while-revalidate and stale-if-error

# CloudFront - TTL configuration hierarchy # Origin Response Headers -> CloudFront TTL -> Browser TTL

# At origin (S3, EC2, Load Balancer), set: Cache-Control: public, max-age=3600, stale-while-revalidate=600, stale-if-error=86400 # max-age=3600: Cache for 1 hour # stale-while-revalidate=600: Serve stale for 10 min while revalidating # stale-if-error=86400: Serve stale for 24h if origin errors

# CloudFront distribution settings: DefaultTTL: 3600 # Used when origin provides no Cache-Control MinTTL: 0 # Minimum cache time MaxTTL: 86400 # Maximum cache time (caps origin max-age)

# Browser TTL via CloudFront: # Set in Cache Policy or add header at edge # Cache-Control: public, max-age=300 # Browser caches for 5 min ```

```bash # Cloudflare - TTL configuration via API # Edge TTL should be longer than browser TTL

curl -X PATCH "https://api.cloudflare.com/client/v4/zones/ZONE_ID/cache/settings" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "browser_cache_ttl": 300, "cache_level": "aggressive", "max_browser_cache_ttl": 3600 }'

# Cloudflare cache rules with TTL hierarchy curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/rules/cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "description": "Static assets TTL", "priority": 1, "action": "cache", "action_parameters": { "edge_ttl": 3600, "browser_ttl": 300, "respect_strong_etags": true }, "expression": "path matches \"^/static/.*\"" }'

# TTL recommendations by content type: # - HTML: 0-300s (frequently updated) # - CSS/JS: 3600-86400s (versioned files) # - Images: 86400-604800s (rarely change) # - API responses: 60-300s (dynamic but cacheable) # - Downloads: 604800+s (static files) ```

Fastly TTL configuration:

```vcl # Fastly VCL - TTL hierarchy

sub vcl_fetch { # Respect origin Cache-Control by default # But apply bounds

# Static assets - long TTL if (req.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|pdf|zip)$") { # Ensure minimum 1 hour, maximum 1 week if (beresp.ttl < 1h) { set beresp.ttl = 1h; } if (beresp.ttl > 168h) { set beresp.ttl = 168h; }

# Add stale-while-revalidate if not present if (beresp.http.Cache-Control !~ "stale-while-revalidate") { set beresp.http.Cache-Control = beresp.http.Cache-Control ", stale-while-revalidate=600"; } }

# API responses - short TTL if (req.url ~ "^/api/") { # Ensure reasonable bounds if (beresp.ttl < 10s) { set beresp.ttl = 10s; } if (beresp.ttl > 300s) { set beresp.ttl = 300s; }

# Allow serving stale on errors set beresp.http.Cache-Control = beresp.http.Cache-Control ", stale-if-error=3600"; }

# HTML pages - very short TTL if (req.url ~ "\.html$" || req.url ~ "/$") { if (beresp.ttl > 300s) { set beresp.ttl = 300s; } }

return (deliver) }

# Handle stale content sub vcl_stale { # Serve stale if backend is slow if (req.http.Fastly-FF ~ "1") { # This is a shield request return (stale); } } ```

### 4. Fix purge propagation

CloudFront invalidation:

```bash # CloudFront - Invalidate paths through shield # Invalidation propagates from edge to shield to origin

# Invalidate specific objects aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/static/css/main.css" "/static/js/app.js"

# Invalidate with wildcard aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/static/*"

# Invalidate all (use sparingly) aws cloudfront create-invalidation \ --distribution-id E1234567890ABC \ --paths "/*"

# Check invalidation status aws cloudfront get-invalidation \ --distribution-id E1234567890ABC \ --id I1234567890ABC

# Invalidation states: # - InProgress: Still propagating # - Completed: Propagated to all edge locations

# Invalidation typically takes 60-300 seconds # Shield cache is invalidated before edge caches

# Best practices: # - Use versioned filenames instead of invalidation # - Invalidate specific paths, not wildcards when possible # - Batch invalidations (100 paths per request) # - Monitor invalidation completion before dependent deploys ```

Cloudflare purge:

```bash # Cloudflare - Purge cache (affects edge and shield)

# Purge by URL (precise) curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "files": [ "https://example.com/static/css/main.css", "https://example.com/static/js/app.js" ] }'

# Purge by prefix (wildcard) curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "prefixes": [ "https://example.com/static/css/", "https://example.com/static/js/" ] }'

# Purge all (nuclear option) curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"purge_everything": true}'

# Purge by host (subdomain-specific) curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \ -H "Authorization: Bearer API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "hosts": ["static.example.com"] }'

# Rate limits: # - URL purge: 30 requests per minute # - Prefix purge: 30 requests per minute # - Purge everything: 3 requests per minute

# Check purge status via Cache Analytics curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/analytics/dashboard" \ -H "Authorization: Bearer API_TOKEN" \ -G --data-urlencode "since=-1h" ```

Fastly soft purge:

```bash # Fastly - Soft purge (allows stale while revalidating)

# Soft purge - serves stale while fetching fresh curl -X POST "https://api.fastly.com/service/SERVICE_ID/purge/API_CACHE_KEY" \ -H "Fastly-Key: FASTLY_API_TOKEN" \ -H "Accept: application/json" \ -H "Surrogate-Key: API_CACHE_KEY"

# Hard purge - removes from cache entirely curl -X PURGE "https://api.fastly.com/service/SERVICE_ID/purge/API_CACHE_KEY" \ -H "Fastly-Key: FASTLY_API_TOKEN" \ -H "Accept: application/json"

# Purge by surrogate key (recommended) # Set Surrogate-Key header at origin for related content # X-Surrogate-Key: page-123 api-user-456

curl -X POST "https://api.fastly.com/service/SERVICE_ID/purge/surrogate/page-123" \ -H "Fastly-Key: FASTLY_API_TOKEN"

# Advantages of surrogate keys: # - Purge multiple related objects with one request # - More efficient than URL-based purge # - Works with dynamic URLs

# Check purge status curl -X GET "https://api.fastly.com/service/SERVICE_ID/stats" \ -H "Fastly-Key: FASTLY_API_TOKEN" \ -G --data-urlencode "from=-5m" ```

### 5. Prevent cache stampede

Implement request coalescing:

```vcl # Fastly VCL - Request coalescing to prevent thundering herd

sub vcl_recv { # Detect potential stampede scenario if (req.http.Fastly-FF && beresp.ttl < 10s) { # This is a shield request for soon-to-expire content # Use request coalescing set req.http.X-Request-Coalesce = "1"; }

return (hash); }

sub vcl_fetch { # For content that might cause stampede if (req.url ~ "^/popular/" || req.url ~ "^/api/trending") { # Add grace period for stale content set beresp.grace = 60s;

# Enable request coalescing # Only one request goes to origin while others wait }

return (deliver); } ```

```yaml # CloudFront - Shield protection settings # Origin Shield automatically provides request coalescing

# Enable Origin Shield in distribution config aws cloudfront update-distribution --id E1234567890ABC \ --if-match ETAG \ --distribution-config '{ "Origins": { "Quantity": 1, "Items": [{ "Id": "origin-s3", "DomainName": "bucket.s3.amazonaws.com", "OriginShield": { "Enabled": true, "OriginShieldRegion": "us-east-1" } }] } }'

# Origin Shield benefits: # - Reduces origin requests by 50-80% # - Automatically coalesces concurrent requests # - Single region acts as request serializer

# Choose shield region based on: # - Origin location (minimize shield-to-origin latency) # - Traffic volume (major CloudFront PoP regions) # - Recommended: us-east-1, eu-west-1, ap-southeast-1 ```

Implement stale-while-revalidate:

```bash # Origin response headers for stampede protection

# HTTP headers from origin Cache-Control: public, max-age=60, stale-while-revalidate=300 # max-age=60: Fresh for 1 minute # stale-while-revalidate=300: Serve stale for 5 min while fetching fresh

# Or with stale-if-error Cache-Control: public, max-age=60, stale-while-revalidate=300, stale-if-error=3600 # stale-if-error=3600: Serve stale for 1 hour if origin errors

# CloudFront respects these headers when DefaultTTL allows

# Nginx origin configuration location /api/ { proxy_pass http://backend;

# Add cache headers proxy_cache_valid 200 1m; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_background_update on; proxy_cache_revalidate on;

# Add stale headers to response add_header X-Cache-Status $upstream_cache_status; } ```

### 6. Debug cache hierarchy issues

Trace cache flow:

```bash # CloudFront - Trace request through cache layers # Use verbose curl to see cache headers

curl -vI https://d1234567890abcdef.cloudfront.net/object.key \ 2>&1 | grep -E "X-Cache|Age|Via|X-Amz-Cf"

# First request (cold): # X-Cache: Miss from cloudfront # Via: 1.1 xxx.cloudfront.net (CloudFront)

# Second request (warm edge): # X-Cache: Hit from cloudfront # Age: 5 # Via: 1.1 xxx.cloudfront.net (CloudFront)

# Third request (after edge expiry, shield hit): # X-Cache: RefreshHit from cloudfront # Age: 65

# Cloudflare - Trace with debug headers curl -vI https://example.com/object.key \ -H "CF-Connecting-IP: 1.2.3.4" \ 2>&1 | grep -E "CF-Cache-Status|CF-Ray|Age"

# Fastly - Trace with timing headers curl -vI https://example.com/object.key \ 2>&1 | grep -E "X-Cache|X-Served-By|X-Timer|Age"

# X-Timer: S1234567890.V123,VE456,VS0,VE12 # V123: VCL version # VE456: Time in edge (456ms) # VS0: Time waiting for shield (0ms = shield hit) # VE12: Time fetching from origin (12ms) ```

Check cache hit ratio:

```bash # CloudFront - Cache hit ratio via CloudWatch aws cloudwatch get-metric-statistics \ --namespace AWS/CloudFront \ --metric-name CacheHitRate \ --Dimensions Name=Distribution,Value=E1234567890ABC \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 300 \ --statistics Average

# Expected cache hit ratios: # - Static assets: >95% # - Dynamic content: 50-80% # - API responses: 30-70% # - HTML pages: 20-50%

# Cloudflare - Cache hit ratio via API curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/analytics/dashboard" \ -H "Authorization: Bearer API_TOKEN" \ -G --data-urlencode "since=-1h" | \ jq '.result.cached_requests | (requests_cached / requests_total * 100)'

# Fastly - Cache hit ratio via Real-Time Stats curl -X GET "https://api.fastly.com/service/SERVICE_ID/stats" \ -H "Fastly-Key: FASTLY_API_TOKEN" \ -G --data-urlencode "from=-1h" | \ jq '[.[] | .cache_hit / (.cache_hit + .cache_miss) * 100] | add / length' ```

Monitor origin shield effectiveness:

```bash # CloudFront - Shield hit ratio # Calculate: (Total Requests - Origin Requests) / Total Requests

TOTAL=$(aws cloudwatch get-metric-statistics \ --namespace AWS/CloudFront \ --metric-name Requests \ --Dimensions Name=Distribution,Value=E1234567890ABC \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 3600 \ --statistics Sum \ --query 'Datapoints[0].Sum')

ORIGIN=$(aws cloudwatch get-metric-statistics \ --namespace AWS/CloudFront \ --metric-name OriginRequests \ --Dimensions Name=Distribution,Value=E1234567890ABC \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 3600 \ --statistics Sum \ --query 'Datapoints[0].Sum')

echo "Shield Hit Ratio: $(echo "scale=2; ($TOTAL - $ORIGIN) / $TOTAL * 100" | bc)%"

# Expected shield hit ratios: # - Without shield: 0% (all requests go to origin) # - With shield, cold cache: 50-70% # - With shield, warm cache: 80-95% ```

Prevention

  • Use versioned filenames instead of purge for static assets
  • Configure shield TTL >= edge TTL to prevent shield misses
  • Implement surrogate keys for efficient purge propagation
  • Enable request coalescing to prevent cache stampede
  • Set stale-while-revalidate headers for graceful revalidation
  • Monitor cache hit ratio and shield effectiveness metrics
  • Document cache key rules for each content type
  • Test purge propagation time in staging environment
  • Use cache warming for predictable high-traffic content
  • Implement origin shield in region closest to origin server
  • **502 Bad Gateway**: Origin connection failed
  • **504 Gateway Timeout**: Origin response timeout
  • **Stale content served**: Cache not invalidated properly
  • **Cache key collision**: Different content same cache key
  • **Origin overload**: Shield not protecting origin effectively