How to Fix Nginx Proxy Cache MISS Issue for REST API Backend


2 views

When your Nginx proxy consistently returns X-Proxy-Cache: MISS headers, it indicates that responses aren't being cached despite your configuration. Let's examine why this happens and how to properly configure caching for REST APIs.

The current setup has several potential problems:

proxy_cache_path /path/to/cache/dir keys_zone=one:60m;
proxy_cache_methods GET HEAD POST;

While POST methods are included, they're generally not cacheable by default due to their non-idempotent nature. More importantly, the backend response headers might be preventing caching.

Here's an improved configuration that handles common caching scenarios:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m 
    inactive=60m use_temp_path=off max_size=1g;

server {
    location / {
        proxy_cache api_cache;
        proxy_cache_key "$scheme$request_method$host$request_uri";
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        
        # Force cache certain responses
        proxy_ignore_headers Cache-Control Expires Set-Cookie;
        proxy_cache_bypass $http_cache_control;
        add_header X-Cache-Status $upstream_cache_status;
        
        proxy_pass http://backend;
    }
}
  • The backend includes Cache-Control: private or no-store headers
  • Responses contain Set-Cookie headers (even if ignored)
  • The request contains Authorization headers
  • Cache key isn't unique enough for your API structure

Add these directives to monitor cache behavior:

log_format cache_log '$remote_addr - $upstream_cache_status [$time_local] '
                     '"$request" $status $body_bytes_sent';
access_log /var/log/nginx/cache.log cache_log;

For testing, use curl with specific headers:

curl -v http://your-api.example.com/api/resource \
-H "Cache-Control: max-age=0" \
-H "Accept: application/json"

For dynamic APIs, consider these patterns:

# Cache based on content type
map $sent_http_content_type $cacheable_type {
    "application/json" 1;
    "text/xml"        1;
    default           0;
}

# Use in location block
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid any 0;

Implement cache purging for API updates:

location ~ /purge(/.*) {
    proxy_cache_purge api_cache "$scheme$request_method$host$1";
    allow 127.0.0.1;
    deny all;
}

For a product API that returns JSON:

location /api/products {
    proxy_cache api_cache;
    proxy_cache_lock on;
    proxy_cache_use_stale error timeout updating;
    proxy_cache_background_update on;
    
    # Cache successful responses for 5 minutes
    proxy_cache_valid 200 5m;
    
    # Different cache times by response code
    proxy_cache_valid 404 302 1m;
    
    proxy_pass http://product-api;
}

When examining your Nginx configuration and headers, several factors could be preventing caching:

# Key problematic areas in current config:
proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie;
proxy_cache_methods GET HEAD POST;

First, let's modify your cache configuration with these critical changes:

proxy_cache_path /path/to/cache/dir levels=1:2 keys_zone=api_cache:60m 
    inactive=24h max_size=1g use_temp_path=off;

server {
    location / {
        proxy_cache api_cache;
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_methods GET;
        proxy_cache_key "$scheme://$host$request_uri";
        proxy_cache_use_stale error timeout updating;
        
        # Handle headers properly
        proxy_ignore_headers Cache-Control;
        proxy_ignore_headers Set-Cookie;
    }
}

To properly test caching, use curl with these commands:

# First request (should be MISS)
curl -I http://yourserver/api/endpoint

# Second request (should be HIT if caching works)
curl -I http://yourserver/api/endpoint

Add these directives to your config for better debugging:

add_header X-Cache-Status $upstream_cache_status;
add_header X-Cache-Key $proxy_cache_key;

log_format cache_log '$remote_addr - $upstream_cache_status [$time_local] '
                     '"$request" $status $body_bytes_sent';
access_log /var/log/nginx/cache.log cache_log;

REST APIs often prevent caching through:

  • Dynamic URLs with timestamp parameters
  • Authentication headers
  • POST requests when only GET should be cached
  • Vary headers from backend

Example of parameter normalization:

proxy_cache_key "$scheme://$host$uri${args}";

For microservices architecture, consider this cache bypass pattern:

map $request_method $cache_bypass {
    default 0;
    POST    1;
    PUT     1;
    DELETE  1;
}

location / {
    proxy_cache_bypass $cache_bypass;
}