Imagine you order the exact same coffee from the same cafe every morning. Now imagine that every single day the barista insisted on asking your name, your order, and your address from scratch, even though nothing changed. That would be slow and a little silly. The web has the same problem: a browser often needs the same logo, the same stylesheet, and the same script over and over. Caching is how the web says, “I already have this, no need to ask again.”
In this article we’ll walk through what HTTP caching is, why it matters, and the small set of headers that control it. You don’t need any prior experience. By the end, you’ll be able to read a response and understand whether it will be cached, for how long, and who is allowed to keep a copy.
What caching actually means
A cache is just a saved copy of something, kept somewhere closer or faster so you don’t have to fetch the original again. In the web world, when your browser downloads a file (an image, a CSS file, a piece of JSON), it can store that file locally. The next time a page asks for the same file, the browser can serve it from its own storage instead of making another trip across the internet.
That trip is the expensive part. Every request has to travel from your device, possibly across the world, to a server, and the response has to travel all the way back. Even on a fast connection that takes time. Multiply it by the dozens of files a typical page needs, and you can see why skipping repeat trips makes pages feel instant.
There are two big wins from caching:
- Speed for the user. Files served from a local cache appear almost immediately, because nothing has to be downloaded.
- Less load for the server. Every request that a cache answers is a request the origin server never has to handle. That saves bandwidth and keeps the server free for work it actually needs to do.
Cache vs. database
A cache and a database both store data, but they have different jobs. A database is the source of truth — the place where the real, permanent data lives. A cache is a temporary, throwaway copy kept only to make things faster. If a cache is wiped, nothing is lost; the data can always be fetched again from the source.
Who keeps the copies
When we say “the cache,” we’re usually talking about one of a few places along the route between a user and a server. It helps to picture them as a chain:
[ Browser ] → [ Proxy / CDN ] → [ Origin server ]
private shared source of truth
- The browser cache lives on your own computer or phone. It’s private — only you use it. This is what makes pages you’ve visited before load faster on a second visit.
- A shared cache sits between many users and the server. A company proxy or, more commonly today, a CDN (Content Delivery Network) keeps one copy and serves it to lots of different people. Because it’s shared, it can dramatically reduce how often the origin server is contacted.
The same response can be cached in more than one of these places at once. The headers we’re about to meet are how a server tells each of them what’s allowed.
Where CDNs fit
CDNs are a deep topic of their own — how they place copies of your files in data centers around the world so users download from a nearby location. We cover CDNs in more detail in the hosting material. For this article, just remember a CDN is a big shared cache that lives between your users and your server.
Cache-Control: the main switch
The most important caching header today is Cache-Control. A server sends it with a response to describe how that response may be cached. It accepts a list of comma-separated instructions called directives. Here’s a typical example:
HTTP/1.1 200 OK
Content-Type: text/css
Cache-Control: public, max-age=86400
Let’s decode the common directives one by one.
| Directive | What it means |
|---|---|
max-age=N |
The response is “fresh” for N seconds. During that time a cache can reuse it with no questions asked. (86400 = 24 hours.) |
public |
Any cache may store this, including shared caches like a CDN. |
private |
Only the user’s own browser may store it. Shared caches must not keep a copy. |
no-cache |
A cache may store it, but must re-check with the server before reusing it. |
no-store |
Do not store this at all, anywhere. Always fetch fresh. |
A few of these names are easy to mix up, so it’s worth slowing down on the last two.
no-cache vs. no-store
The names sound almost identical, but they do very different things.
no-storemeans keep no copy whatsoever. Every time the file is needed, it must be downloaded again from the server. You’d use this for genuinely sensitive or always-changing data, like a bank balance page.no-cacheis more relaxed. It says, you can keep a copy, but you must ask the server “is my copy still good?” before you use it. If the server says the copy is still valid, the browser reuses it without re-downloading — which is fast. If not, it downloads the fresh version.
no-cache does not mean don't cache
This trips up almost everyone at first. no-cache does not forbid caching. It allows caching but requires revalidation before each use. If you truly want nothing stored, the directive you want is no-store.
public vs. private
public and private decide who gets to keep the copy, not how long. A bundled stylesheet that’s identical for everyone can be public, so a CDN can serve one copy to thousands of users. A page showing John Doe’s personal account details should be private, so only John’s own browser keeps it and no shared cache accidentally hands it to Jane Doe.
Expires: the older approach
Before Cache-Control existed, the way to set an expiry was the Expires header. It names an exact date and time after which the response should be considered stale:
Expires: Wed, 21 Oct 2026 07:28:00 GMT
It works, but it has a weakness: it depends on the clock. If the user’s computer clock is wrong, the calculation goes wrong too. Cache-Control: max-age avoids this by counting seconds from the moment the response arrives, which doesn’t care what time anyone thinks it is. When both headers are present, Cache-Control wins. You’ll still see Expires in the wild, but new work should prefer max-age.
ETag and revalidation: the “is it still good?” check
So far we’ve handled freshness with timers. But there’s a smarter trick for when a cached copy might be out of date: instead of blindly re-downloading, the browser can ask, “has this actually changed?” If it hasn’t, the server can say so in a tiny reply and skip sending the whole file again. This is called revalidation, and the star of it is the ETag.
An ETag (entity tag) is a short identifier the server attaches to a file — think of it as a fingerprint or version stamp. If the file changes even slightly, the ETag changes too. Here’s how a first request looks:
HTTP/1.1 200 OK
ETag: "a1b2c3d4"
Cache-Control: no-cache
Content-Type: image/png
(... the full image bytes ...)
The browser stores the image along with its ETag. Later, when it needs the image again, it asks the server to confirm the copy is still current by sending the ETag back in an If-None-Match header:
GET /logo.png HTTP/1.1
Host: blog.acy-partner.com
If-None-Match: "a1b2c3d4"
Now one of two things happens:
If the file is UNCHANGED:
Server replies → 304 Not Modified (no body, just "use your copy")
If the file HAS CHANGED:
Server replies → 200 OK + the new file + a new ETag
That 304 Not Modified response is the magic. It’s tiny — no file body at all — so it travels back almost instantly, and the browser then reuses the copy it already had. You get the safety of always checking with the server, without paying the cost of re-downloading when nothing changed.
| Status | Meaning in caching |
|---|---|
200 OK |
Here is the full, fresh file. |
304 Not Modified |
Your cached copy is still good — reuse it, I’m sending nothing. |
A common, effective combo
A widely used setup is Cache-Control: no-cache together with an ETag. The browser keeps a copy but checks every time, and most of those checks come back as a small 304. You get fresh-enough content with very little data transferred.
There’s an older relative of this pattern based on dates rather than fingerprints: the server sends a Last-Modified date, and the browser asks “anything newer?” with an If-Modified-Since header. It works the same way and also results in a 304 when nothing changed. ETags are usually preferred because they detect any change, not just changes a clock can see.
Putting it together: a real-world strategy
For a typical website you’ll often see two different caching styles, chosen by what kind of file it is:
- Files that change often (like an HTML page, or an API response) get
no-cachewith anETag. They’re always re-checked, but the check is cheap. - Files that rarely change (like a logo, a font, or a versioned script named
app.4f8a.js) get a longmax-agewithpublic. The browser and CDN keep them for a long time and don’t bother the server at all.
The trick with the second group is that when you do need to change such a file, you change its name (for example by putting a new hash in the filename). A new name is a new URL, so caches treat it as a brand-new file and fetch it fresh — no stale copies hanging around. This pattern is called cache busting, and it lets you cache aggressively without ever serving outdated content.
Quick recap
Caching is the web’s way of not repeating work, and it pays off in two directions at once: faster pages for people, and less load for servers. Here are the ideas worth keeping:
- A cache is a temporary saved copy, kept somewhere faster than the original.
- Copies can live in your browser (private) or in a shared cache like a CDN.
Cache-Controlis the main header. Remembermax-age(how long it’s fresh),public/private(who may keep it),no-cache(keep but revalidate), andno-store(keep nothing).Expiresis the older, clock-based way to set freshness; prefermax-age.- ETag +
If-None-Matchlet a browser ask “has this changed?” and get a tiny304 Not Modifiedwhen it hasn’t — saving a full download.
Once these click, a good next step is to look at how a CDN spreads cached copies across the globe, and how response headers in general shape what a browser does. Caching sits right at the intersection of speed and correctness, and understanding it is one of the quickest ways to make a site feel noticeably faster.