cPanelOps · Hosting guides for site operators
cPanelOpsv2.2.0
Plain-English cPanel help from the team behind FirstResponderHost
26 guides live
CpanelOps / PageOps

Home › Guides

Performance

Browser caching headers: let visitors keep your static files

A few lines in .htaccess tell browsers to reuse images, styles and scripts instead of downloading them on every page. What to set, and what never to cache.

The first time someone visits your site, their browser downloads the logo, the stylesheet, the scripts and the fonts. On the second page, it needs all of them again. Whether it reuses the copies it already has, or fetches every one of them a second time, depends on a header your server sends with each file. On many small sites that header is missing, and every page view pays the full price.

How it works

With each file, the server can send a Cache-Control header that says how long the browser may keep and reuse it without asking again. A stylesheet sent with a lifetime of a week is downloaded once and then read from the visitor's own disk for the next seven days. Nothing is requested, so nothing can be faster.

The same header is read by proxies and content delivery networks, which will also hold the file and serve it on your behalf.

See what you send now

curl -I https://yourdomain.com/path/to/style.css

Look for a cache-control or expires line. If neither is there, browsers fall back on guesswork. In the browser's developer tools, the Network tab shows the same headers for each file, and on a reload it marks files that came "from disk cache" or "from memory cache".

Set sensible lifetimes

Add this to the .htaccess file in public_html. It works on Apache and on LiteSpeed:

<IfModule mod_expires.c>
  ExpiresActive On

  # Pages: always check for a fresh copy
  ExpiresByType text/html "access plus 0 seconds"

  # Images and fonts: rarely change
  ExpiresByType image/jpeg "access plus 1 month"
  ExpiresByType image/png "access plus 1 month"
  ExpiresByType image/webp "access plus 1 month"
  ExpiresByType image/svg+xml "access plus 1 month"
  ExpiresByType font/woff2 "access plus 1 month"

  # Styles and scripts: change when you update the site
  ExpiresByType text/css "access plus 1 week"
  ExpiresByType application/javascript "access plus 1 week"
  ExpiresByType text/javascript "access plus 1 week"
</IfModule>

The IfModule wrapper means that if the server lacks this feature the lines are skipped, instead of breaking the site. After saving, repeat the curl -I test and confirm the header now appears.

The rule: cache files, not pages

Notice that HTML is set to zero. Your pages are the part that changes: a new article, a corrected headline, an updated price. If a browser were told to keep a page for a week, a returning reader would see last week's version and you would have no way to reach them. Let pages be checked every time, and let the heavy, unchanging files they refer to be kept.

"Checked every time" is cheap. When the page has not changed, the server answers with a tiny "not modified" response and no content.

The catch: changed files

If the stylesheet is cached for a week and you change it today, returning visitors keep the old one until their copy expires. The page and its styling are briefly out of step. There are two standard ways around it:

  • Add a version to the address. Link to style.css?v=12 and raise the number whenever the file changes. To the browser it is a new address, so it fetches it at once. Content systems and their plugins do this automatically.
  • Put the version in the file name, such as style.a41f.css. Build tools generate these names. Files named this way can safely be cached for a year, because any change produces a new name.

If you edit files by hand and do neither, keep the lifetime for styles and scripts short, a day or less, and accept slightly less benefit.

What should never be cached

  • Pages that differ per person: account pages, baskets, admin screens. Content systems mark these as private themselves; do not override that with a blanket rule for all files.
  • Files that crawlers must always see fresh, such as robots.txt and ads.txt. They are small, and stale copies cause confusing delays when you change them.
  • Responses to form submissions.

This is why the example sets lifetimes by file type, instead of one rule for everything.

Compression, the companion setting

Caching avoids repeat downloads. Compression makes the first download smaller. Text files such as HTML, CSS and JavaScript shrink to a fraction of their size when the server compresses them in transit. Most cPanel hosts switch this on already; the Optimize Website page in cPanel controls it where it is offered. To check, look for a content-encoding line with gzip or br in the response headers of a page. Images are already compressed and gain nothing from it.

How this fits with a page cache or CDN

These are three different layers, and they stack:

  • A page cache on the server saves the work of building each page.
  • A CDN saves the journey to your server for static files.
  • Browser caching saves the request altogether on repeat views.

If a cache plugin or CDN is already setting these headers, let it. Two sources setting different lifetimes for the same files is a common cause of "I changed it and nothing happened".

Common questions

I updated an image and visitors still see the old one.

It is cached under the same name. Upload the new version under a new file name and update the page to point at it. That works instantly for everyone.

Does a long cache lifetime use the visitor's disk space?

Browsers manage their cache within a fixed budget and discard the least recently used files by themselves. You cannot fill someone's disk.

How do I see my own changes while working?

With the developer tools open, tick "Disable cache" on the Network tab, or use a private window. A normal reload may still use cached files.

Spotted a mistake, or a step that has changed?

cPanel's screens differ a little between versions and hosts. Tell us at info@firstresponderhost.com and we will correct the guide.

More guides