How to Cache Static Assets with Nginx for Faster Websites

Web2026-09-21TryQuickToolBox

Why Static Asset Caching Matters

Every time a user visits your website, their browser requests dozens of static assets: CSS files, JavaScript, images, fonts, and more. Without proper caching, each request hits your server, increasing load times and bandwidth usage. Nginx, a high-performance web server, can dramatically improve this by telling browsers to cache these assets locally and by caching them on the server side.

In this guide, you'll learn how to configure Nginx to cache static assets effectively, reducing latency and server load.

Understanding Browser Caching vs. Server-Side Caching

There are two main types of caching relevant here:

Both are crucial for performance. We'll cover both.

Step 1: Configure Browser Caching with Expires Headers

The simplest way to enable browser caching is by adding expires directives to your Nginx configuration. This sets the Expires and Cache-Control headers.

Open your site's configuration file (e.g., /etc/nginx/sites-available/example.com) and add a location block for static assets:

location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

This tells the browser to cache these files for one year. The immutable directive indicates the file will never change, so the browser won't even revalidate on reload.

Best practice: Use versioned filenames (e.g., style.abc123.css) so you can update assets without breaking caching. When the file changes, the filename changes, and the browser fetches the new version.

Step 2: Enable Server-Side Caching with Proxy Cache

If you're running an application server (like Node.js, Python, or PHP) behind Nginx, you can cache static responses in Nginx to reduce backend requests.

First, define a cache path in the http block of nginx.conf:

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

Then, in your server block, use it:

location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg)$ {
    proxy_cache static_cache;
    proxy_cache_valid 200 302 1y;
    proxy_cache_valid 404 1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_lock on;
    add_header X-Cache-Status $upstream_cache_status;
    proxy_pass http://backend;
}

This caches successful responses for one year and serves stale content if the backend is down. The X-Cache-Status header helps you debug (HIT, MISS, etc.).

Step 3: Optimize with Compression and HTTP/2

Caching reduces requests, but you can further speed up transfers by enabling gzip compression and HTTP/2.

Enable gzip in nginx.conf:

gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_min_length 1000;

HTTP/2 multiplexes requests and is enabled by adding http2 to your listen directive:

listen 443 ssl http2;

Note: HTTP/2 requires HTTPS. If you haven't set up TLS yet, see our guide on how HTTPS and TLS work.

Comparison: Cache-Control Directives

Directive Meaning Recommended For
public Response can be cached by any cache Static assets
private Response is for a single user User-specific data
no-cache Must revalidate with server HTML pages
no-store Never cache Sensitive data
immutable Content will not change Versioned assets

Step 4: Test and Verify

After applying changes, reload Nginx: sudo nginx -s reload. Then test with curl:

curl -I https://example.com/style.css

Look for Cache-Control and Expires headers. You can also use browser DevTools (Network tab) to see if assets are served from cache (Status 200 or 304).

For server-side cache, monitor the X-Cache-Status header.

Common Pitfalls and Best Practices

FAQ

How do I know if my assets are being cached?

Check response headers with curl -I or browser DevTools. Look for Cache-Control: public, max-age=31536000 and Expires headers. In DevTools, the Size column will show "(from disk cache)" for cached assets.

What's the difference between expires and Cache-Control?

expires sets an absolute date, while Cache-Control uses relative seconds. Cache-Control takes precedence in modern browsers. Nginx's expires directive sets both for compatibility.

Can I cache dynamic content?

Yes, but with caution. Use proxy_cache with short TTLs and consider cache keys based on cookies or headers. For user-specific content, use private or avoid caching.

Speed Up Your Workflow with TryQuickToolBox

While optimizing your site's performance, you might need to compress images or PDFs. Try our Image Compressor to reduce image sizes without losing quality, complementing your Nginx caching strategy.