Nginx Cheatsheet

Caching

Use this Nginx reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Proxy Cache Setup

http {
    # Define a cache zone (put this in http {}, outside server {})
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=1g
                     inactive=60m
                     use_temp_path=off;
}
ParameterExampleMeaning
path/var/cache/nginxDirectory for cache files
levels1:22-level subdirectory hierarchy (avoids huge single dirs)
keys_zonemy_cache:10mName and shared memory size for keys
max_size1gMax disk usage; LRU eviction when exceeded
inactive60mRemove items not accessed within this window
use_temp_pathoffWrite directly to cache dir (avoids extra copy)

Enabling Cache in a Location

location / {
    proxy_pass http://backend;

    proxy_cache            my_cache;
    proxy_cache_valid      200 302  10m;    # Cache 200/302 for 10 min
    proxy_cache_valid      301      1h;
    proxy_cache_valid      404      1m;
    proxy_cache_use_stale  error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_lock       on;              # Prevent thundering herd on cold cache
    proxy_cache_min_uses   1;              # Cache after 1 request

    add_header X-Cache-Status $upstream_cache_status;
}

$upstream_cache_status Values

ValueMeaning
HITServed from cache
MISSNot in cache; fetched from upstream
EXPIREDIn cache but expired; re-fetched
STALEServed stale copy while re-fetching
UPDATINGStale; background update in progress
REVALIDATEDCache validated with upstream (304)
BYPASSCache bypassed (see below)

Bypassing Cache

# Bypass when client sends Pragma: no-cache or Cache-Control: no-cache
map $http_cache_control $bypass {
    "no-cache" 1;
    default    0;
}

location / {
    proxy_cache my_cache;
    proxy_cache_bypass  $bypass $cookie_session;   # Bypass if either is non-empty
    proxy_no_cache      $bypass $cookie_session;   # Don't store response either
}

Common bypass variables: $cookie_session, $http_authorization, $arg_nocache.

Serving Stale Content

proxy_cache_use_stale error timeout updating
                      http_500 http_502 http_503 http_504;

# Extend stale serving time even if upstream is reachable
proxy_cache_background_update on;
proxy_cache_revalidate         on;   # Use conditional GET (If-Modified-Since)

Cache Key

# Default key: scheme + method + host + URI
proxy_cache_key "$scheme$request_method$host$request_uri";

# Custom key (ignore query string)
proxy_cache_key "$host$uri";

# Include auth for per-user caching (expensive — large key space)
proxy_cache_key "$host$uri$http_authorization";

Cache Purge

Nginx OSS has no built-in purge. Cache files are extensionless, named by the MD5 of the cache key (there is no *.cache suffix), stored under the levels hierarchy.

# Path for one URL: md5 of the proxy_cache_key value
echo -n "httpGETexample.com/page" | md5sum
# → 8f7e...c2a1b  →  with levels=1:2 the file lives at
#   /var/cache/nginx/b/a1/8f7e...c2a1b  (last char / previous 2 chars)

# Or grep for the KEY line embedded in each cache file
grep -lr "KEY: httpGETexample.com/page" /var/cache/nginx | xargs rm -f

# Nuke the whole cache (nginx recreates entries on demand)
rm -rf /var/cache/nginx/*

Use the ngx_cache_purge module for PURGE method support, or use Nginx Plus (proxy_cache_purge).

FastCGI Cache (PHP-FPM)

http {
    fastcgi_cache_path /var/cache/nginx/fastcgi
                       levels=1:2 keys_zone=php_cache:10m max_size=512m;
}

server {
    set $skip_cache 0;
    if ($request_method = POST) { set $skip_cache 1; }
    if ($query_string != "")    { set $skip_cache 1; }
    if ($request_uri ~* "wp-admin|login") { set $skip_cache 1; }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        include fastcgi_params;

        fastcgi_cache            php_cache;
        fastcgi_cache_valid 200  1h;
        fastcgi_cache_bypass     $skip_cache;
        fastcgi_no_cache         $skip_cache;
        add_header X-Cache       $upstream_cache_status;
    }
}

Browser Cache (via expires)

location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

location ~* \.(html)$ {
    expires -1;                             # no-cache
    add_header Cache-Control "no-store";
}
expiresCache-Control result
1hmax-age=3600
1ymax-age=31536000
-1no-cache
epochno-cache (date in the past)
maxmax-age=315360000
offNo header added