Skip to main content

Cache-Control Headers

Set the right Cache-Control headers on your origin for optimal caching with ModPageSpeed 2.0.

Requires ModPageSpeed 2.0.x or later with conditional revalidation support. Without conditional revalidation, must-revalidate causes full origin re-fetches on every stale request. Upgrade before following these recommendations.

Your origin’s Cache-Control headers directly control how ModPageSpeed caches and serves your content. Getting these right eliminates stale content surprises and minimizes unnecessary origin traffic.

When your origin sends no Cache-Control header, ModPageSpeed applies configurable defaults (HTML: no-cache, CSS/JS: 300s, images: 1800s in safe mode) and logs a warning. Setting explicit headers is better than relying on defaults.

HTML Pages

Cache-Control: public, max-age=60, must-revalidate
ETag: "content-hash-or-version"

HTML references hashed assets. A deploy changes HTML content to point at new asset URLs. Short max-age=60 provides one minute of caching. With conditional revalidation, stale requests are cheap — origin returns 304 when content hasn’t changed.

Use max-age=0 only when absolute real-time freshness is required (stock tickers, live scores). Every request with max-age=0 triggers a conditional revalidation, which means an origin round-trip even when content hasn’t changed.

Hashed Static Assets (CSS/JS with Fingerprints)

Cache-Control: public, max-age=31536000, immutable

The URL changes on every build. Content at a given URL never changes. ModPageSpeed respects immutable and caps the max-age at pagespeed_immutable_max_age (default: 7 days).

Non-Hashed Static Assets

Cache-Control: public, max-age=3600, must-revalidate
ETag: "file-mtime-and-size"

The URL is stable but content may change. Short max-age with must-revalidate keeps content fresh. Conditional revalidation makes the revalidation cheap.

User-Uploaded Images

Cache-Control: public, max-age=86400

User images rarely change at the same URL. 24-hour caching is reasonable. If images can be replaced at the same URL, add must-revalidate and an ETag.

API Responses and Dynamic Content

Cache-Control: no-store

ModPageSpeed does not cache no-store responses. The response passes through unchanged.

Framework Configuration

FrameworkWhere to Set HeadersHTMLHashed Assets
nginx (static)nginx.conf per locationmax-age=60, must-revalidatemax-age=31536000, immutable
Apache.htaccess Header setmax-age=60, must-revalidatemax-age=31536000, immutable
Next.js 13+next.config.js headers()max-age=60, must-revalidateAutomatic for _next/static/
Astro 4+Server adapter headersmax-age=60, must-revalidateAutomatic via content hashing
Rails 7+config.public_file_server.headersmax-age=60, must-revalidateAutomatic via Propshaft
WordPressCache plugin or .htaccessmax-age=60, must-revalidateTheme-dependent

PURGE vs Natural Expiration

PURGE immediately invalidates all cached variants for a URL. Use it for urgent content corrections. For routine deploys, let must-revalidate handle freshness naturally — conditional revalidation is cheaper than purge-and-rebuild because it preserves optimized image variants (AVIF, WebP) when only the HTML changed.

Cache Mode

The pagespeed_cache_mode directive controls how ModPageSpeed assembles Cache-Control headers on optimized responses. Safe mode (the default) adds must-revalidate and uses short TTLs for quick recovery from misconfigurations. Aggressive mode uses long TTLs with public for maximum cache efficiency. See Cache Modes for full details.

ModPageSpeed Directives

These directives control the default max-age values applied when origin responses lack a Cache-Control header. They do not override explicit origin headers.

pagespeed_html_max_age 0;      # Default for HTML (seconds, default: 0 = no-cache)
pagespeed_css_max_age 300;     # Default for CSS/JS (seconds, default: 300)
pagespeed_image_max_age 1800;  # Default for images (seconds, safe: 1800, aggressive: 86400)

When pagespeed_html_max_age is 0 and the origin sends no Cache-Control, HTML responses are served with Cache-Control: no-cache — the client must revalidate on every request.

Conditional Revalidation

pagespeed_conditional_revalidation on;   # default: on

When enabled, stale cache entries with stored ETag or Last-Modified values trigger conditional requests (If-None-Match / If-Modified-Since) instead of full re-fetches. On 304, the cached content is refreshed without re-downloading or re-optimizing. Disable only if your origin mishandles conditional requests.

Browser Force-Refresh

pagespeed_force_refresh_html on;   # default: on
pagespeed_force_refresh off;       # default: off

When a user performs a force-refresh (Ctrl+F5 or Shift+Reload), the browser sends Cache-Control: no-cache or Pragma: no-cache. ModPageSpeed detects these signals and forces revalidation against your origin, even if the cached entry is fresh.

For HTML, this is enabled by default — users expect force-refresh to fetch the latest page. For images, CSS, and JS, it is off by default to prevent unnecessary origin load. Enable pagespeed_force_refresh if you want force-refresh to apply to all content types.

When conditional revalidation is also enabled (the default), force-refresh uses If-None-Match / If-Modified-Since to avoid re-downloading unchanged content.

Auditing Your Origin

The web console flags URLs where the origin sends no Cache-Control header. Use the URL inspector to check freshness status and identify origins that need explicit headers.