Cache TTL
Control how long cached resources stay fresh before the browser or CDN must re-validate.
Metrics
This technique can move up to 67% of LCP's share of the Lighthouse score.
This technique does not move TBT.
This technique does not move CLS.
This technique can move up to 67% of FCP's share of the Lighthouse score.
This technique can move up to 67% of SI's share of the Lighthouse score.
This technique can move up to 67% of TTFB's share of the Lighthouse score.
Overview
Cache Time To Live (TTL) defines how long a cached copy of a resource is considered fresh — whether in the browser, a CDN edge node, or a server-side page cache. Setting TTL correctly means returning visitors get instant responses from cache while still seeing up-to-date content when something changes.
Impact grade, A+ to D: how much of the Lighthouse score this technique can move. Unaffected: TBT, CLS.
The Problem
If the TTL is too short, browsers re-download assets that have not changed, wasting bandwidth and adding latency on every visit. If it is too long, users may see stale content after a deployment because their browser is still serving an old version that has not expired. Most WordPress sites default to zero or very short TTLs, getting neither benefit.
Performance Impact
LCP
High
cached images and hero assets eliminate download round trips
FCP
High
cached CSS, fonts and scripts serve instantly on repeat visits
SI
longer cache lifetimes mean more assets are already cached on return visits, reducing visual completion time
TTFB
High
page cache TTL serves full HTML from edge without touching the origin
Risk: Medium
stale content if cache busting is absent for long-TTL assets
How it works
01
Server attaches TTL directives to the response
The origin server sets
Cache-Control: max-age=<seconds>(or anExpiresdate) on each response. This instructs browsers, CDNs, and any intermediate proxy how long to consider the resource fresh.02
Caches serve the resource without contacting the origin
While the cached copy is within its TTL window, any request for that resource is answered directly from the cache — browser disk, CDN edge node, or server-side object cache. No DNS lookup, no TCP, no origin processing.
03
At expiry, the cache re-validates or re-fetches
Once the TTL expires, the cache treats the copy as stale. If the server supports conditional requests (
ETag/Last-Modified), it can reply with304 Not Modifiedand a fresh TTL — sending no body. Otherwise a full re-download occurs.04
Different layers need different TTL strategies
Browser cache, CDN edge cache, server-side page cache (like Redis or Varnish), and object cache all have independent TTL configuration. A consistent strategy aligns their durations with the update frequency of each content type.
.htaccess — per-type TTL (Apache)
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType application/font-woff2 "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresDefault "access plus 2 days"
</IfModule>WordPress appends version query strings to CSS and JS, making long TTLs safe. Images and fonts almost never change — 1 year is standard.
Object cache TTL — WordPress
<?php
// Cache expensive query results for 1 hour
wp_cache_set( 'my_data_key', $results, 'my_group', HOUR_IN_SECONDS );
$data = wp_cache_get( 'my_data_key', 'my_group' );
if ( false === $data ) {
$data = expensive_db_query();
wp_cache_set( 'my_data_key', $data, 'my_group', HOUR_IN_SECONDS );
}WordPress defines MINUTE_IN_SECONDS, HOUR_IN_SECONDS, DAY_IN_SECONDS, and WEEK_IN_SECONDS constants. Use them for readability.
Limitations
- Longer TTLs improve performance but risk users seeing stale content. Shorter TTLs ensure freshness but reduce cache effectiveness. The balance depends on how frequently each content type changes.
- Pages containing user-specific data (cart contents, logged-in account pages, personalised recommendations) must use very short TTLs or be explicitly excluded from caching to avoid serving one user’s data to another.
- TTL settings only govern resources served from your domain. Assets loaded from external CDNs, Google Fonts, or analytics providers use whatever cache headers those third-party servers set.
Common Mistakes
Applying the same TTL to all resource types
Images and fonts change rarely — 1-year TTLs are safe. HTML changes with every update — short TTLs or no-cache + ETag are appropriate. JS and CSS depend on your version strategy. One-size-fits-all TTL is always suboptimal.
Setting very short TTLs for truly static assets
Caching versioned CSS/JS or images for just a few minutes negates most of the benefit. If the asset URL includes a version string, the URL changes when the content changes — making long TTLs completely safe.
No cache busting strategy for updated assets
If a file is updated but its URL stays the same, browsers continue serving the old cached version until TTL expires. WordPress handles this for enqueued scripts via $version parameters — ensure any custom includes do the same.
Confusing max-age and s-maxage
max-age applies to all caches including private browser caches. s-maxage applies only to shared caches (CDNs, proxies) and overrides max-age for those layers. Use s-maxage when you want different TTLs for the browser vs the CDN edge.
Need help applying this to your site?
Targeted fixes, not guesswork. We identify which techniques move the needle for your specific WordPress stack.
Start an Engagement