# Nginx Cache Not Working

Nginx's proxy cache should store responses and serve them quickly, but cache misses happen on every request. Cached content doesn't update, or the wrong content gets cached. Pages that should be fast remain slow because the cache isn't doing its job.

Understanding Nginx Cache Mechanics

  1. 1.Nginx proxy cache works by:
  2. 2.Computing a cache key from request attributes
  3. 3.Checking if a cached response exists
  4. 4.Serving cached content if valid
  5. 5.Otherwise, fetching from upstream and storing

Cache problems occur when: - Cache key doesn't match - Cache zone not configured - Upstream headers prevent caching - Cache bypass conditions trigger

Check if cache is being used: ``bash # Add cache status to response header curl -I http://example.com/api/data | grep X-Cache-Status

Possible values: HIT, MISS, BYPASS, EXPIRED, UPDATING, REVALIDATED, STALE.

Common Cause 1: Cache Zone Not Configured

The cache zone must be defined before it can be used.

Problematic config: ``nginx location /api { proxy_pass http://backend; proxy_cache mycache; # mycache not defined }

Solution: Define cache zone in http block: ```nginx http { # Define cache zone proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m max_size=100m inactive=60m use_temp_path=off;

server { location /api { proxy_pass http://backend; proxy_cache mycache; proxy_cache_valid 200 10m; add_header X-Cache-Status $upstream_cache_status; } } } ```

Verify cache directory exists: ``bash ls -la /var/cache/nginx/ # If missing, create it sudo mkdir -p /var/cache/nginx sudo chown www-data:www-data /var/cache/nginx

Common Cause 2: Cache Key Mismatch

Every request generates a different cache key, preventing reuse.

Default cache key: $scheme$proxy_host$request_uri

Problem: Query parameters change: `` /api/data?page=1 /api/data?page=1&sort=name /api/data?page=1&_=12345 # Cache buster

Each is a different key, no cache hit.

Solution: Normalize cache key: ```nginx location /api { proxy_pass http://backend; proxy_cache mycache;

# Ignore certain query parameters proxy_cache_key "$scheme$host$request_uri";

# Or be more specific proxy_cache_key "$scheme$host$uri$is_args$args";

# Or ignore all query params proxy_cache_key "$scheme$host$uri"; } ```

Better: Ignore cache busters: ```nginx # Remove _ timestamp parameter before caching map $args $cleaned_args { ~(^|.*&)_(=[^&]*)?(.*)$ $3; default $args; }

location /api { proxy_cache_key "$scheme$host$uri?$cleaned_args"; } ```

Common Cause 3: Upstream Headers Prevent Caching

Backend sends headers that prevent caching.

Check backend headers: ``bash curl -I http://backend:3000/api/data

Look for: - Cache-Control: no-cache, no-store, private - Expires in the past - Set-Cookie (Nginx doesn't cache responses with cookies by default) - Vary with too many headers

Solution: Override caching headers: ```nginx location /api { proxy_pass http://backend; proxy_cache mycache; proxy_cache_valid 200 10m;

# Ignore upstream cache headers proxy_ignore_headers Cache-Control Expires Set-Cookie;

# Force caching proxy_cache_valid any 10m;

add_header X-Cache-Status $upstream_cache_status; } ```

But be careful with cookies - you might cache personalized content: ``nginx # Only cache if no cookie proxy_cache_bypass $http_cookie;

Common Cause 4: Wrong Cache Validity Settings

proxy_cache_valid determines what gets cached and for how long.

Problematic config: ``nginx proxy_cache_valid 200 10m; # Only caches 200 responses, not 301, 302, 404, etc.

Solution: Define validity for each status: ``nginx proxy_cache_valid 200 302 10m; proxy_cache_valid 301 1h; proxy_cache_valid 404 1m; proxy_cache_valid any 1m; # Cache everything else briefly

Or rely on upstream headers: ``nginx proxy_cache_valid 200 10m; # Use Cache-Control max-age from upstream proxy_cache_max_range_offset 0; # Honor upstream max-age

Common Cause 5: Cache Bypass Conditions

Certain conditions skip the cache entirely.

Problematic config: ``nginx location /api { proxy_cache mycache; proxy_cache_bypass $http_pragma; # Bypass on Pragma header proxy_cache_bypass $http_authorization; # Bypass if Authorization header }

Common bypass triggers: - Authorization header present - Pragma: no-cache - Cache-Control: no-cache from client - Admin/logged-in user cookies

Solution: Control bypass conditions: ```nginx location /api { proxy_cache mycache;

# Only bypass for specific conditions proxy_cache_bypass $cookie_nocache $arg_nocache;

# Don't bypass for Authorization - if content is same for all users proxy_cache_bypass off;

add_header X-Cache-Status $upstream_cache_status; } ```

Common Cause 6: Cache Lock Issues

Concurrent requests for same uncached content all hit backend.

Problem: `` Request 1: MISS -> fetches from backend -> caches Request 2: MISS (simultaneous) -> also fetches from backend Request 3: MISS (simultaneous) -> also fetches from backend

Solution: Enable cache lock: ```nginx location /api { proxy_cache mycache; proxy_cache_valid 200 10m;

proxy_cache_lock on; proxy_cache_lock_timeout 5s;

add_header X-Cache-Status $upstream_cache_status; } ```

Now only first request fetches; others wait for cache.

Common Cause 7: Vary Header Expansion

Vary header causes multiple cache entries for same URL.

Backend sends: `` Vary: Accept-Encoding, Accept-Language

Nginx creates separate cache entries for each combination: `` Accept-Encoding: gzip, Accept-Language: en Accept-Encoding: gzip, Accept-Language: fr Accept-Encoding: identity, Accept-Language: en

Solution: Limit Vary or normalize headers: ```nginx location /api { proxy_cache mycache;

# Ignore Vary header from upstream proxy_ignore_headers Vary;

# Or handle Vary yourself proxy_cache_key "$scheme$host$uri$http_accept_encoding"; } ```

Common Cause 8: Stale Content Not Updating

Cache serves outdated content after backend changes.

Problem: ``nginx proxy_cache_valid 200 10m; # Content cached for 10 minutes, backend updated after 2 minutes # Users see old content for 8 more minutes

Solution 1: Background update: ```nginx location /api { proxy_cache mycache; proxy_cache_valid 200 10m;

# Serve stale content while updating in background proxy_cache_use_stale updating; proxy_cache_background_update on;

add_header X-Cache-Status $upstream_cache_status; } ```

Solution 2: Proactive purge: ```nginx location /api { proxy_cache mycache;

# Allow manual cache purge proxy_cache_purge $http_purge_method; }

# Purge cache with: curl -X PURGE http://example.com/api/data ```

Solution 3: Shorter validity: ``nginx proxy_cache_valid 200 1m;

Common Cause 9: Cache Not Persisting

Cache directory permissions or disk space issues.

Diagnosis: ```bash # Check cache directory permissions ls -la /var/cache/nginx/

# Check disk space df -h /var/cache

# Check cache files ls -la /var/cache/nginx/

# Count cached files find /var/cache/nginx -type f | wc -l ```

Solution: ```bash # Fix permissions sudo chown -R www-data:www-data /var/cache/nginx

# Create cache directory if missing sudo mkdir -p /var/cache/nginx sudo chown www-data:www-data /var/cache/nginx ```

Verification Steps

  1. 1.Add cache status header:
  2. 2.```nginx
  3. 3.add_header X-Cache-Status $upstream_cache_status;
  4. 4.`
  5. 5.Make multiple requests:
  6. 6.```bash
  7. 7.# First request
  8. 8.curl -I http://example.com/api/data | grep X-Cache-Status
  9. 9.# MISS

# Second request curl -I http://example.com/api/data | grep X-Cache-Status # HIT ```

  1. 1.Check cache files:
  2. 2.```bash
  3. 3.ls -la /var/cache/nginx/
  4. 4.`
  5. 5.Monitor cache logs:
  6. 6.```nginx
  7. 7.log_format cache '$remote_addr - $upstream_cache_status [$time_local] '
  8. 8.'$request $status $body_bytes_sent';

access_log /var/log/nginx/cache.log cache; ```

  1. 1.Check cache statistics:
  2. 2.```nginx
  3. 3.# Add to server block
  4. 4.location /cache_status {
  5. 5.set $cache_hits $sent_http_x_cache_hits;
  6. 6.return 200 "Cache status: $upstream_cache_status\n";
  7. 7.}
  8. 8.`

Complete Working Configuration

```nginx http { proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:50m max_size=500m inactive=60m use_temp_path=off;

# Ignore cache-busting query params map $args $clean_args { ~(^|.*&)_(=[^&]*)?(.*)$ $3; default $args; }

server { location /api { proxy_pass http://backend;

proxy_cache api_cache; proxy_cache_key "$scheme$host$uri?$clean_args"; proxy_cache_valid 200 10m; proxy_cache_valid 404 1m; proxy_cache_valid any 5m;

proxy_cache_lock on; proxy_cache_use_stale updating error timeout http_500 http_502 http_503 http_504; proxy_cache_background_update on;

proxy_ignore_headers Set-Cookie; add_header X-Cache-Status $upstream_cache_status;

add_header Set-Cookie ""; # Clear any cookies } } } ```

Quick Reference

X-Cache-StatusMeaningWhat to Check
MISSNot in cacheCache zone exists? Cache key consistent?
BYPASSCache skippedCache bypass conditions? Authorization headers?
EXPIREDWas cached, now expiredIncrease validity time or background update
UPDATINGStale content while updatingproxy_cache_use_stale configured
STALEServing old content due to backend errorIntentional fallback behavior
HITFound in cacheWorking correctly

Cache issues usually come from configuration gaps: missing zones, inconsistent keys, or upstream headers that prevent caching. Check the X-Cache-Status header to understand what's happening.