Skip to main content

Confirm

Are you sure?

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"
}

Monitor this automatically

Full Stack Audit runs this check and 126 others on your site on a schedule, and emails you when one that passed starts failing.

More Performance checks