Website Performance

Browser Caching & Compression

Two of the cheapest speed wins on the web. Caching stops repeat visitors from re-downloading your files, and compression shrinks what first-time visitors download. Both are mostly a matter of getting a few HTTP headers right.

Junaid KhalidUpdated 8 min readAdvancedIncludes a 11 min video

The short answer

Browser caching uses the Cache-Control header to tell browsers how long to keep a file, so repeat visits load it from disk instead of the network. Cache fingerprinted static assets for a year with public, max-age=31536000, immutable, and revalidate HTML with no-cache. Compression (Gzip or Brotli) shrinks text files such as HTML, CSS and JavaScript before they are sent. Brotli is usually the smaller of the two.

Together they cut bytes for new visitors and eliminate downloads for returning ones, which lowers Time to First Byte and helps LCP.

  • Cache hashed assets for a year with immutable; revalidate HTML with no-cache.
  • Cache busting (fingerprinted filenames) makes long caching safe.
  • Compress text with Brotli or Gzip. Never compress images.

Two Jobs, Two Mechanisms

Caching

Helps repeat visitors. Once a file is cached, the browser reuses its local copy instead of asking the server again, so the second page view and every return visit are far faster. You control it with the Cache-Control header.

Compression

Helps first-time visitors. The server compresses text files before sending them and the browser unpacks them, so far fewer bytes travel the network. Gzip is the universal baseline; Brotli is usually smaller.

The Cache-Control Directives

Caching comes down to one header with a handful of directives. These are the ones you will actually use.

DirectiveWhat it does
max-age=SECONDSHow long the file is fresh before the browser must check again
publicAny cache may store it, including shared CDN caches
privateOnly the user's browser may store it, not shared caches
no-cacheStore it, but revalidate with the server before each use
no-storeNever store it anywhere (for sensitive data)
immutableThe file will never change, so skip revalidation entirely

A Caching Policy by Resource Type

Different files want different rules. The guiding idea: cache things that do not change at the same URL for as long as possible, and revalidate things that do.

ResourceSuggested policyWhy
Hashed assets (app.3f8a.js)public, max-age=31536000, immutableFilename changes on deploy, so cache for a year safely
Images and fontspublic, max-age=31536000 (if versioned)Rarely change; long cache, fingerprint if they do
HTML pagesno-cache or a short max-ageContent changes at the same URL, so always revalidate
API responsesno-store or a small max-ageOften personal or fast-changing; usually not cacheable

Cache Busting

Long caching and fresh content seem to be in conflict: if you cache a file for a year, how do visitors get your update? Cache busting resolves it. You put a content hash in the filename, so the moment the file changes, its URL changes too.

styles.css becomes styles.8f2c1a.css. When you deploy a change, the hash becomes something new, the browser sees a URL it has never cached, and it downloads the fresh file. Everything you did not change keeps its long-lived cache. This is why fingerprinted assets can safely use immutable and a one-year max-age. Modern build tools add these hashes for you.

Gzip vs Brotli

Both compress text before it is sent. The browser advertises what it supports and the server picks one. In practice: use Brotli where you can, fall back to Gzip.

GzipBrotli
CompressesText: HTML, CSS, JS, SVG, JSONText: HTML, CSS, JS, SVG, JSON
Relative size on textBaselineAbout 15 to 25% smaller
Browser supportUniversalAll modern browsers
Best forA safe default everywherePrefer when host or CDN supports it

Cache-Control, Explained

DebugBear, a web-performance monitoring company, walks through the Cache-Control header directive by directive and shows what each one does to repeat-visit speed. A practical companion to the tables above.

DebugBear: how Cache-Control headers work, from max-age to immutable, and how to set a policy that speeds up repeat visits.Video: Cache-Control Headers: Browser Caching Explained by DebugBear. Loads from YouTube only when you press play.

A Worked Example

Illustrative scenario, not client data

Lighthouse flags two issues on a marketing site: "Serve static assets with an efficient cache policy" and "Enable text compression". Here is the fix, in order.

  1. Turn on compression: enable Brotli at the server or CDN for HTML, CSS and JavaScript, with Gzip as the fallback. First-load payloads drop immediately.
  2. Fingerprint the assets: confirm the build outputs hashed filenames so each asset URL changes when its content does.
  3. Set long caching on those assets: apply public, max-age=31536000, immutable to the hashed CSS, JS, fonts and images.
  4. Keep HTML fresh: set HTML to no-cache so visitors always get the current page, while the heavy assets stay cached.

Both Lighthouse audits pass. First-time visitors download fewer bytes, repeat visitors skip the asset downloads entirely, and the lighter server work nudges Time to First Byte down. Nothing on the page changed visually; it just got faster.

Tune the headers, then watch the search data.

Caching and compression are a server job. Whether the speed-up helped is a search-data question. Murkuz connects Google Search Console, Analytics 4 and Bing to Claude or ChatGPT, read-only, so you can ask whether clicks, impressions and position for your pages moved after the change, and confirm Google recrawled them. It reads your data; it does not change your site.

See the Search Console connector

Common Mistakes

  • Long-caching HTML

    Applying a year-long max-age to HTML means visitors see an old page until the cache expires. HTML should revalidate, with no-cache or a short max-age. Long caching is for fingerprinted assets only.

  • Caching aggressively without fingerprints

    A long max-age on a file whose URL never changes will serve stale code after a deploy. Only cache for a year when the filename changes with the content.

  • Compressing images

    Running Gzip or Brotli over already-compressed JPEGs and PNGs wastes CPU for no gain. Compress text; optimise images with the right format instead.

  • Forgetting the CDN layer

    Setting headers on the origin but leaving a CDN misconfigured means the edge may not cache or compress at all. Check what the CDN actually returns, not just the origin.

Further reading

The primary sources on HTTP caching and compression, Google and MDN documentation first. Each one was read in full before it was listed here.

  • Official docsweb.dev, Jeff Posnick & Ilya Grigorik

    HTTP caching

    The clearest practical primer on cache headers: how max-age, ETag and revalidation interact, and the long-cache-plus-fingerprint pattern for static assets. The foundation for this page.

  • Official docsMDN Web Docs

    HTTP caching (guide)

    MDN's reference on how the browser cache decides freshness, what private versus public means, and how conditional requests return a 304 instead of re-downloading.

  • ReferenceMDN Web Docs

    Cache-Control header reference

    The exact definition of every Cache-Control directive: max-age, no-cache, no-store, immutable, must-revalidate and the rest, with what each one actually does.

  • Official docsweb.dev

    Reduce payloads with text compression

    Why text compression matters, which file types benefit, and how Gzip and Brotli cut the bytes sent for HTML, CSS and JavaScript.

  • GuideDebugBear

    The ultimate guide to browser caching

    A practitioner walkthrough of caching headers with real examples and screenshots, and a good companion to the video from the same channel.

Frequently Asked Questions

What is the difference between Gzip and Brotli?

Both are compression algorithms that shrink text-based files such as HTML, CSS, JavaScript, SVG and JSON before the server sends them. Brotli is newer and usually compresses text about 15 to 25 percent smaller than Gzip at comparable settings, so it sends fewer bytes. Every modern browser supports both, and the browser tells the server which it accepts through the Accept-Encoding header. If your host or CDN offers Brotli, prefer it and fall back to Gzip. Neither helps with images or video, which are already compressed.

What Cache-Control header should I set for static assets?

For versioned static files such as a bundled script named app.3f8a9c.js, use Cache-Control: public, max-age=31536000, immutable. That caches the file for a year and tells the browser it will never change, so repeat visits load it instantly from disk with no network request. This is safe only when the filename changes whenever the content changes, which is what cache busting provides. For HTML and other files that change at the same URL, use a short max-age or no-cache so visitors always get the current version.

What is cache busting?

Cache busting is putting a content hash or version into a file's name or query string, for example styles.8f2c1a.css, so the URL changes whenever the file changes. It lets you cache aggressively, often for a year, without the risk of serving a stale file: when you deploy new code, the filename changes, the browser sees a new URL and downloads the fresh version, while unchanged files keep their long-lived cache. Most modern build tools add these fingerprints automatically.

What is the difference between no-cache and no-store?

They sound similar but do opposite things. no-store tells the browser never to keep a copy of the response at all, which suits private or sensitive data. no-cache allows the browser to store the response but requires it to revalidate with the server before using it, usually with an ETag or Last-Modified check that returns a lightweight 304 Not Modified when nothing has changed. no-cache is not the same as no caching: it means cache, but always check first.

Does browser caching help SEO or Core Web Vitals?

Indirectly. Caching and compression are not direct ranking factors, but they make pages faster, and page experience, including Core Web Vitals, is a signal Google's ranking systems consider. Caching speeds up repeat visits and navigation, and reducing payloads and server work lowers Time to First Byte, which feeds into LCP. The main benefit is real: faster pages for actual users, which tends to help engagement and conversions as much as rankings.

Does a CDN improve performance?

Usually, yes. A content delivery network caches your assets on servers close to the visitor, so files travel a shorter distance and the browser gets them faster. A CDN also reduces the load on your origin server and lowers Time to First Byte for visitors far from your host. Most CDNs handle compression and long-lived caching for static assets with little configuration, which is why a CDN is often the single easiest performance win for a global audience.

Should I compress images with Gzip or Brotli?

No. Formats such as JPEG, PNG, WebP and AVIF are already compressed, so running Gzip or Brotli over them adds work for almost no saving and can even make them slightly larger. Reserve text compression for HTML, CSS, JavaScript, SVG and JSON. To make images smaller, use a modern format and the right dimensions instead, which is covered in our image SEO guide.

Shipped a faster server? Confirm it in your data.

Connect Google Search Console once and ask, from Claude or ChatGPT, whether your pages gained clicks and impressions after the change and whether Google has recrawled them. Read-only, through the official APIs.