HomeMethod

Knowledge

Web VitalsTechniquesSnippetsAnswersImpactsBottlenecks

Cache TTL

Control how long cached resources stay fresh before the browser or CDN must re-validate.

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

  1. 01

    Server attaches TTL directives to the response

    The origin server sets Cache-Control: max-age=<seconds> (or an Expires date) on each response. This instructs browsers, CDNs, and any intermediate proxy how long to consider the resource fresh.

  2. 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.

  3. 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 with 304 Not Modified and a fresh TTL — sending no body. Otherwise a full re-download occurs.

  4. 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)

Code
<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

Code
<?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.

Test this techniqueCache AnalysisRun the tool

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
Support our family now