Nginx caching cheatsheet.

proxy_cache_path

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

Define once in http {} block.

Use in server / location

location / {
    proxy_pass http://app;
    
    proxy_cache app_cache;
    proxy_cache_key "$scheme$host$request_uri";
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_lock on;
    proxy_cache_revalidate on;
    add_header X-Cache-Status $upstream_cache_status;
}

Cache statuses

$upstream_cache_status:

  • MISS: not in cache, fetched.
  • HIT: served from cache.
  • EXPIRED: stale, refetched.
  • STALE: stale served (use_stale).
  • UPDATING: stale served while update in progress.
  • BYPASS: cache bypassed.
  • REVALIDATED: revalidated with origin.

Cache key

proxy_cache_key "$scheme$host$request_uri$is_args$args";
proxy_cache_key "$scheme$host$request_uri$cookie_lang";

Include vary headers in key to avoid cross-contamination.

Bypass cache

proxy_cache_bypass $cookie_logged_in $http_pragma;
proxy_no_cache $cookie_logged_in $http_pragma;

Don’t cache for logged-in users.

Microcaching

Cache for very short time to absorb spikes:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=micro:10m max_size=1g inactive=10m;

location / {
    proxy_pass http://app;
    proxy_cache micro;
    proxy_cache_valid 200 1s;
    proxy_cache_use_stale updating;
    proxy_cache_lock on;
}

1s cache, but cache_lock + use_stale updating prevents thundering herd.

Respect backend headers

proxy_cache_valid any 0;       # default; let backend headers control

Backend sends Cache-Control: max-age=..., nginx honors it.

proxy_ignore_headers Cache-Control Expires Set-Cookie;
proxy_cache_valid 200 5m;

Or ignore + force.

Conditional GET

proxy_cache_revalidate on;

Nginx sends If-Modified-Since / If-None-Match. Backend can return 304 → cache extended.

Purging cache

Open source nginx doesn’t include purge. Workarounds:

  • Delete files in cache path matching key.
  • Use ngx_cache_purge module (rebuild).
  • Use a wrapper / orchestrator.
# Find key hash
KEY="https example.com /page"
HASH=$(echo -n $KEY | md5sum | awk '{print $1}')
# Last 2 chars / next 2 chars / hash (depending on levels)
rm /var/cache/nginx/{0..f}/{0..f}/<hash>

Practical: just rm -rf /var/cache/nginx/* && nginx -s reload.

fastcgi_cache (for PHP-FPM)

fastcgi_cache_path /var/cache/nginx/fcgi levels=1:2 keys_zone=fcgi:50m;

location ~ \.php$ {
    fastcgi_cache fcgi;
    fastcgi_cache_key "$scheme$host$request_uri";
    fastcgi_cache_valid 200 60m;
    fastcgi_pass unix:/run/php/php-fpm.sock;
    include fastcgi_params;
}

Static file caching

location ~* \.(css|js|woff2|jpg|png|svg|webp)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

sendfile + tcp_nopush

sendfile on;
tcp_nopush on;
sendfile_max_chunk 1m;

Kernel-level file send.

open_file_cache

open_file_cache max=10000 inactive=60s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

Cache file descriptors. Big speedup for static heavy.

Vary handling

proxy_cache_key "$scheme$host$request_uri$cookie_region";

Or include Vary headers in key explicitly via map.

Gzip + cache

Gzip after caching (cache the uncompressed):

gzip_proxied any;

Avoid caching gzipped + non-gzipped variants separately.

brotli

Requires ngx_brotli module:

brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json image/svg+xml;

Common mistakes

  • Caching responses with Set-Cookie (mixes users).
  • No cache lock → thundering herd.
  • Cache key without query string → wrong content served.
  • Caching dynamic per-user content.
  • Cache path on slow disk (use SSD or tmpfs).

Read this next

If you want my caching presets, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .