Set a long browser cache lifetime for static files in WordPress. That is the quick fix. CSS, JavaScript, images, fonts, and icons should stay in the visitor’s browser for weeks or months. This cuts repeat load time and helps remove the PageSpeed warning: Serve static assets with an efficient cache policy.

TLDR: Tell browsers to keep static assets for at least 30 days, or even 1 year when files are versioned. For example, a WooCommerce shop with 2.4 MB of images and scripts may cut repeat page load size by 60% to 80% after proper caching. If 1,000 returning users visit each week, that can mean thousands of fewer file requests. Use a cache plugin, server rules, or a CDN to set this up.

What does this warning mean?

Browsers are lazy in a good way. They like to reuse files they already downloaded. If your logo, theme CSS, font files, and scripts do not change often, the browser should not fetch them every time.

When Google PageSpeed Insights says “Serve static assets with an efficient cache policy”, it means this:

  • Your static files expire too soon.
  • The browser has to ask the server for them again.
  • Your page feels slower than it should.
  • Your server does extra work for no good reason.

Honestly, it feels like paying rent twice for the same apartment. The file is already there. Let the browser use it.

Check System Date and Time

What are static assets?

Static assets are files that rarely change. WordPress sites use many of them.

  • Images: JPG, PNG, WebP, SVG, GIF.
  • Stylesheets: CSS files from themes and plugins.
  • Scripts: JavaScript files.
  • Fonts: WOFF, WOFF2, TTF.
  • Icons: favicon files and icon sets.

These files can be cached safely. The trick is to cache them for a long time, while still allowing updates when files change.

The best cache policy for WordPress static files

A solid rule is simple:

  • Images: cache for 30 days to 1 year.
  • CSS and JS: cache for 30 days to 1 year.
  • Fonts: cache for 1 year.
  • HTML pages: cache carefully, often for shorter periods.

For static assets, many sites use this header:

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

That means:

  • public: browsers and CDNs may store the file.
  • max-age=31536000: keep it for 1 year.
  • immutable: the file will not change at the same URL.

The last part matters. If you use immutable, the file URL should change when the file changes. WordPress often does this with version numbers, like this:

/wp-content/themes/mytheme/style.css?ver=1.2.4

When the version changes, the browser sees a new file. No drama. No stale CSS. No angry client asking why the button is still purple.

Option 1: Use a WordPress cache plugin

This is the easiest path for most users. A good cache plugin can set browser cache headers for you.

Popular choices include:

  • LiteSpeed Cache, great if your host uses LiteSpeed.
  • WP Rocket, easy and polished.
  • W3 Total Cache, powerful but a bit crowded.
  • Cache Enabler, light and simple.

Plugin menus vary. Expect to waste time clicking through tabs with names like Browser Cache, File Optimization, or Expires Headers. It drives me crazy that one setting can hide three levels deep.

Look for settings such as:

  • Enable browser caching
  • Add expires headers
  • Set cache control headers
  • Cache static files

Set the lifetime to at least 30 days. If your CSS, JS, fonts, and image URLs change when updated, use 1 year.

Option 2: Add Apache rules in .htaccess

If your WordPress site runs on Apache, you can add rules to the .htaccess file. Back it up first. One tiny typo can make your site throw a tantrum.

<IfModule mod_expires.c>
  ExpiresActive On

  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/webp "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"

  ExpiresByType text/css "access plus 1 year"
  ExpiresByType application/javascript "access plus 1 year"
  ExpiresByType text/javascript "access plus 1 year"

  ExpiresByType font/woff "access plus 1 year"
  ExpiresByType font/woff2 "access plus 1 year"
</IfModule>

<IfModule mod_headers.c>
  <FilesMatch "\.(css|js|jpg|jpeg|png|gif|webp|svg|woff|woff2)$">
    Header set Cache-Control "public, max-age=31536000, immutable"
  </FilesMatch>
</IfModule>

Save the file. Clear your WordPress cache. Then test again.

Option 3: Add Nginx cache headers

If your server runs Nginx, use a location block. You may need hosting support for this. Many shared hosts do not let users edit Nginx config files.

location ~* \.(css|js|jpg|jpeg|png|gif|webp|svg|woff|woff2)$ {
  expires 365d;
  add_header Cache-Control "public, max-age=31536000, immutable";
}

Reload Nginx after changes. Do not guess here. If you are not sure, ask your host. A bad server config can break more than caching.

Option 4: Use a CDN

A CDN can serve static files from servers closer to your visitors. It can also set cache rules.

Common CDN tools include:

  • Cloudflare
  • Bunny CDN
  • KeyCDN
  • Amazon CloudFront

In your CDN settings, create a rule for static file types. Cache them for 1 month to 1 year. Many small sites do fine with Cloudflare’s standard caching plus a page rule or cache rule for assets.

Be careful with logged-in WordPress pages, carts, checkout pages, and admin pages. Those are not regular static files. Do not cache them like images.

How to test if it worked

Use these tools:

  • Google PageSpeed Insights
  • GTmetrix
  • WebPageTest
  • Chrome DevTools

In Chrome, open DevTools. Go to Network. Click a CSS, JS, image, or font file. Check Response Headers. You want to see something like this:

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

You may also see:

Expires: Wed, 21 Oct 2026 07:28:00 GMT

That is fine. Cache-Control is the modern boss. Expires is still useful for older setups.

Common mistakes that slow WordPress down

  • Caching HTML for too long: This can show old prices, old posts, or old cart data.
  • Not changing file versions: Visitors may see stale CSS or broken layouts.
  • Caching third-party scripts: You often cannot control ads, trackers, or social widgets.
  • Using too many plugins: More files can mean more requests.
  • Ignoring fonts: Font files are often large and great cache candidates.

What about query strings?

WordPress often adds versions to files with query strings, like ?ver=6.5. Some old advice says to remove them. Do not rush.

Version strings help browsers know when a file changed. That is useful. A better plan is to keep versioning and set long cache times. If your CDN dislikes query strings, change the CDN setting or use filename-based versioning through your build tool or optimization plugin.

A simple setup for most WordPress sites

Here is a safe plan:

  1. Install a trusted cache plugin.
  2. Enable browser caching.
  3. Set static assets to cache for 1 year.
  4. Keep file versioning turned on.
  5. Use WebP images where possible.
  6. Add a CDN if your audience is spread across regions.
  7. Test in PageSpeed Insights and Chrome DevTools.

This setup is simple. It is also effective. Returning visitors get a much lighter page. Your server gets fewer repeat requests. Your site feels snappier without changing the design.

Final tip

Do not chase a perfect score all day. Fix the big stuff first. Static asset caching is one of those big wins. It is boring, yes. But boring speed fixes are often the best kind.

Set long cache headers. Keep asset URLs versioned. Test the result. Then enjoy a faster WordPress site with fewer wasted requests.