Performance · Checked on every page
Proper caching headers for static assetsWhat it checks and how to fix it
What it checks
Sends a HEAD request to the first 10 stylesheets, scripts and images the page references and looks for a Cache-Control, Expires or ETag header on each. Passes when every asset has one, warns when some assets have none, and fails when none of them do.
Why it matters
Without caching headers the browser cannot tell whether a file it already downloaded is still good, so it fetches the same CSS, JavaScript and images again on every page view. That adds latency to every navigation, wastes your visitors' bandwidth and puts avoidable load on your origin server.
How to fix it
Send a Cache-Control header on every static asset. Fingerprinted files (whose name changes when their content does, like app-3f9a1c.css) can be cached for a year; files with fixed names should get a shorter lifetime plus an ETag so the browser can revalidate them cheaply.
nginx
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
location /images/ {
add_header Cache-Control "public, max-age=86400";
etag on;
}
Apache (mod_expires and mod_headers)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
</IfModule>
Cloudflare: Caching → Configuration → Browser Cache TTL, or a Cache Rule matching your asset paths with "Browser TTL" set to override the origin.
Rails (config/environments/production.rb)
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=31536000, immutable"
}