Performance

Browser Caching Explained: Cache-Control, ETag and Expires

Browser caching lets returning visitors reuse files instead of downloading them again. Learn Cache-Control, ETag and Expires, plus safe lifetimes per file type.

By Zebra Hosting Cloud

Every file on your page, from the logo to the main stylesheet, has to reach the visitor's browser. Browser caching lets the browser keep a copy and reuse it on later page views or return visits instead of downloading it again. Done well, it makes the second page a visitor opens feel almost instant and cuts the load on your server.

Caching is controlled by HTTP response headers your server sends with each file. This guide explains the important ones and gives sensible values for typical sites.

How the browser decides

When the browser needs a file, it asks itself two questions. Do I already have a copy? And is that copy still fresh? If it is fresh, the browser uses it without contacting the server at all. If it is stale, the browser can either download the file again or ask the server whether the copy is still valid. That question is called revalidation, and a server can answer "not modified" with a tiny response instead of resending the whole file.

Cache-Control

Cache-Control is the main header and the one to learn first. Common directives:

  • max-age=N: the file is fresh for N seconds. For example, max-age=31536000 means one year.
  • public: shared caches such as a CDN may store it as well as the browser.
  • private: only the visitor's own browser may store it. Use this for responses that are personal.
  • no-cache: the browser may store the file but must revalidate with the server before using it. The name is misleading.
  • no-store: nothing should be stored at all, which is right for sensitive pages.
  • immutable: tells the browser the file will never change at that URL, so it need not revalidate even on reload.

Expires, ETag and Last-Modified

Expires is the older way to set a fixed expiry date. If both are present, Cache-Control: max-age takes priority, so you only need Expires for very old clients.

ETag and Last-Modified help with revalidation. The server sends a fingerprint or a timestamp with the file. When the browser asks again, it sends it back (using If-None-Match or If-Modified-Since), and the server replies with a small 304 Not Modified when nothing changed.

Recommended lifetimes

File typeSuggested policyWhy
Images, fonts, videoLong, such as one month to one yearRarely change
CSS and JavaScript with versioned namesOne year, immutableNew version gets a new URL
CSS and JavaScript with fixed namesShorter, or use revalidationYou cannot force an update
HTML pagesNo-cache or short max-ageContent changes and links to new assets
Account, basket, admin pagesprivate or no-storePersonal or sensitive

The cache busting problem

Long cache times create a risk: you update your stylesheet, but returning visitors keep the old one. The standard solution is to change the file's URL whenever its content changes, by adding a version query string such as style.css?v=2 or, better, a content hash in the file name. Build tools and many WordPress plugins do this automatically. HTML pages should stay short-lived so that they can point to the new URLs.

Setting headers on Apache or Nginx

On Apache, the mod_expires and mod_headers modules let you set caching rules per file type in a .htaccess file. For example, you can give images a long expiry and leave HTML alone. On Nginx, the expires and add_header directives do the same. In Plesk, additional Apache or nginx directives can be set per domain (see the knowledge base for panel guides). WordPress caching plugins can write these rules for you, so check what yours already does before adding more.

Check what you are really sending

  1. Open Chrome DevTools, go to the Network tab and reload the page.
  2. Click a file and read the Response Headers section for Cache-Control, ETag and Expires.
  3. Reload again and watch the Size column: entries marked "disk cache" or "memory cache" were served locally.
  4. Run PageSpeed Insights, which flags static files with short or missing cache lifetimes.

Common mistakes

  • Caching HTML for a long time and then wondering why edits are not visible.
  • Using no-store everywhere out of caution, which wastes bandwidth.
  • Forgetting that a CDN has its own cache and needs purging when files change.
  • Sending a long cache time for a file with a fixed name that is updated often.

Caching and the back button

Caching also affects what visitors see when they press the back button. Browsers keep a back-forward cache of recent pages and may show them exactly as they were left. This is usually welcome, but it can surprise people on pages with sensitive data, such as an account page after logging out. For those pages, send Cache-Control: no-store so that nothing sensitive is held in the cache.

Compression works alongside caching

Caching avoids a download; compression makes the download smaller when it does happen. Make sure text files such as HTML, CSS, JavaScript and SVG are compressed with gzip or Brotli. Most hosting setups support both, and CDNs usually apply compression automatically. Check the Content-Encoding header in the Network tab to confirm.

Caching is the cheapest speed-up

Browser caching costs nothing and benefits every returning visitor. Zebra Hosting Cloud hosting runs on the Plesk control panel, where you can manage web server settings per domain,. Pair browser caching with the CDN option for even better results.