Cache static files with expires and Cache-Control in Tengine
2026-09-30
Your pages load slowly even though the server is fast — the bottleneck is usually the browser downloading the same CSS and JS on every visit. The fix is static caching: tell the browser to keep those files for weeks. Tengine makes this a location block with two directives.
The standard static cache block
location ~* \.(css|js|png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
access_log off;
}
What this does:
expires 30d— adds bothExpiresandCache-Control: max-ageheaders (theadd_headerline is belt-and-braces for older clients)access_log off— static hits stop filling your access log (this makes log rotation last longer too)
The hashed-filename upgrade
Long caches have one risk: after a deploy, users keep old JS. If your build pipeline appends a content hash to filenames (app.a3f9c2.js), the filename changes on every deploy, so you can cache aggressively and never worry:
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
The immutable hint tells browsers not to even revalidate hashed assets.
What not to cache
- HTML pages — unless you run a fully static blog and use compression + a CDN, cache HTML briefly (or not at all) so updates show up
- Admin paths, API responses — stale data there is worse than slow
Verify
curl -I https://yourdomain.com/css/style.css
Look for cache-control: public, max-age=2592000 and an expires header ~30 days out.
Summary
One location block caches all static assets for a month, cutting repeat-load time dramatically. Combined with Brotli and HTTP/2, your static site stops being a round-trip problem entirely.