Without Cache-Control headers, browsers fall back to heuristic caching — typically caching responses for 10% of their apparent age, or not at all. Every repeat visitor re-downloads content that hasn't changed, wasting bandwidth and adding latency on every navigation. Properly configured caching reduces server load, speeds up repeat visits, and improves CDN hit rates. ISO 25010 performance-efficiency identifies caching as a core mechanism for resource utilization. The absence of cache headers forces both browsers and CDNs into conservative or undefined behavior, leaving significant performance on the table.
Medium because missing cache headers cause preventable latency on repeat visits and increase server load, but the site remains functional — users are slowed, not blocked.
Set Cache-Control explicitly on every response type. Match the directive to the content's mutability:
# Static assets (images, fonts, versioned JS/CSS)
add_header Cache-Control "public, max-age=31536000, immutable" always;
# HTML pages (content changes; revalidate)
add_header Cache-Control "public, max-age=3600, must-revalidate" always;
# Authenticated or personalized pages
add_header Cache-Control "private, no-store" always;
In Next.js API routes: res.setHeader('Cache-Control', 'public, max-age=3600, s-maxage=86400'). For app/ router, use export const revalidate = 3600 in route segments. Confirm with curl -sI https://yoursite.com | grep -i cache-control.
ID: site-health-check.performance-signals.cache-headers
Severity: medium
What to look for: Extract the Cache-Control header value from the final document response. Count the number of cache directives present (e.g., max-age, s-maxage, no-cache, no-store, public, private, must-revalidate). Parse any max-age or s-maxage value to determine the cache duration in seconds.
Pass criteria: A Cache-Control header is present on the main document response with at least 1 recognized cache directive. The header value must not be empty. Report the directive(s) found and the max-age value if present (e.g., "Cache-Control: public, max-age=3600 — 1 hour cache").
Fail criteria: No Cache-Control header on the main document response, or the header value is empty.
Skip (N/A) when: The response includes Set-Cookie headers, as caching responses with cookies can leak user data and servers may intentionally omit caching in this case.
Detail on fail: "No Cache-Control header on main document — browsers cannot cache optimally"
Remediation: Cache-Control headers let browsers and CDNs cache responses, reducing load times on repeat visits. Add appropriate caching for your content:
# Static marketing pages — cache for 1 hour, revalidate
Cache-Control: public, max-age=3600, must-revalidate
# Dynamic app pages — no cache
Cache-Control: no-store
In Next.js, static pages get caching automatically. For custom headers, use next.config.js headers() or set them in API routes: res.setHeader('Cache-Control', 'public, max-age=3600'). Verify with: curl -sI {URL} | grep -i cache-control