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.
| Directive | What it does |
|---|---|
| max-age=SECONDS | How long the file is fresh before the browser must check again |
| public | Any cache may store it, including shared CDN caches |
| private | Only the user's browser may store it, not shared caches |
| no-cache | Store it, but revalidate with the server before each use |
| no-store | Never store it anywhere (for sensitive data) |
| immutable | The 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.
| Resource | Suggested policy | Why |
|---|---|---|
| Hashed assets (app.3f8a.js) | public, max-age=31536000, immutable | Filename changes on deploy, so cache for a year safely |
| Images and fonts | public, max-age=31536000 (if versioned) | Rarely change; long cache, fingerprint if they do |
| HTML pages | no-cache or a short max-age | Content changes at the same URL, so always revalidate |
| API responses | no-store or a small max-age | Often 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.
| Gzip | Brotli | |
|---|---|---|
| Compresses | Text: HTML, CSS, JS, SVG, JSON | Text: HTML, CSS, JS, SVG, JSON |
| Relative size on text | Baseline | About 15 to 25% smaller |
| Browser support | Universal | All modern browsers |
| Best for | A safe default everywhere | Prefer 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.
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.
- 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.
- Fingerprint the assets: confirm the build outputs hashed filenames so each asset URL changes when its content does.
- Set long caching on those assets: apply public, max-age=31536000, immutable to the hashed CSS, JS, fonts and images.
- 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 connectorCommon 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 cachingThe 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 referenceThe 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 compressionWhy 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 cachingA 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.