Browser caching explained: Cache-Control, ETags and expiry
When a visitor moves from your home page to your services page, their browser does not need to download your logo, stylesheet and fonts again. At least, it should not. Whether it does depends on a few lines of instructions your server sends with every file, known as HTTP caching headers. Get them right and repeat visits and page-to-page navigation feel close to instant. Get them wrong and visitors either download the same files over and over, or keep seeing an outdated page after you update it. This is browser caching explained without jargon overload: how the browser decides, what the key headers mean, and what each type of file should use.
How a browser cache works
The browser keeps a local store of files it has downloaded. For each new request, it goes through a simple decision:
- Do I have a stored copy? If not, download it.
- Is my copy still fresh? If the server said it can be reused for a certain time and that time has not passed, use it without contacting the server at all.
- If stale, can I check whether it changed? Ask the server; if unchanged, the server replies briefly and the browser keeps using its copy.
- If changed or not checkable, download the new version.
Step two is the fastest outcome, since no network request happens. Step three still makes a request but avoids downloading the file body. Everything about caching configuration is about pushing requests towards those two outcomes where appropriate.
Cache-Control: the main instruction
The Cache-Control response header tells browsers and any shared caches in between, such as CDNs and proxies, how to treat a file. It can hold several directives separated by commas.
max-age
Sets how long, in seconds, a response stays fresh. max-age=3600 means one hour; max-age=31536000 means one year. During that period, the browser reuses the file without asking.
no-cache
Despite the name, this does not forbid caching. It means "store it, but check with the server before each reuse". It pairs well with validators so unchanged files cost only a small response.
no-store
This is the one that truly forbids storing the response anywhere. Use it for sensitive data such as account pages, order details or anything containing personal information.
public and private
private allows only the visitor’s own browser to store the response, not shared caches. public explicitly allows shared caches like CDNs to store it too. Pages that differ per logged-in user should be private or no-store.
immutable
Signals that the file at this URL will never change, so the browser should not revalidate it even when the visitor reloads the page. It only makes sense for files whose name changes whenever their content does.
s-maxage
Works like max-age but applies only to shared caches, letting a CDN keep a copy longer or shorter than browsers do.
Expires: the older header
Before Cache-Control existed, servers used an Expires header containing a fixed date and time after which the file is stale. It still works, but if both headers are present, Cache-Control’s max-age takes priority. Expires also depends on clocks being in sync, which is one reason max-age replaced it. You will still see it in older server configurations and some hosting control panels.
Validation: ETag and Last-Modified
When a cached copy is stale, the browser can ask whether it is still valid rather than downloading everything again. It needs a validator for that.
ETag
An ETag is an identifier the server assigns to a particular version of a file, often derived from its content or modification details. The browser stores it and, when revalidating, sends it back in an If-None-Match request header. If the file has not changed, the server responds with status 304 Not Modified and no body. If it has changed, the server sends a normal 200 response with the new file and a new ETag.
Last-Modified
The server can also send the file’s modification date. The browser returns it in an If-Modified-Since header, and the same 304 or 200 logic applies. ETags are generally more precise, because timestamps can change without content changing, or vice versa within the same second.
A load-balancer gotcha
On sites served by several servers, some web server configurations generate ETags using details specific to each machine. The same file can then have different ETags on each server, causing unnecessary full downloads. Developers typically configure ETags to use content-based values or rely on Last-Modified instead.
What should and should not be cached
The right policy depends on how often a file changes and whether it is personal.
- CSS and JavaScript with versioned file names: long max-age, such as one year, plus immutable.
- CSS and JavaScript without versioning: shorter max-age or no-cache with an ETag, so updates reach visitors.
- Web fonts: long max-age; they rarely change.
- Images, logos and icons: long max-age if file names change when replaced; otherwise days rather than a year.
- HTML pages for public content: no-cache with validation, or a short max-age, so edits and price changes show promptly.
- Logged-in dashboards, carts, checkout and order pages: private, no-store.
- API responses with personal data: no-store.
- Downloadable PDFs such as price lists: moderate max-age, as they tend to be updated in place.
Checking your current caching headers
Open Chrome DevTools, go to the Network tab and reload the page. Click any file and look at the Response Headers for Cache-Control, Expires, ETag and Last-Modified. Reload again without disabling cache: files served from cache show "(memory cache)" or "(disk cache)" in the Size column, and revalidated files show a 304 status.
PageSpeed Insights also lists static files with short lifetimes under "Serve static assets with an efficient cache policy".
Putting a sensible policy in place
On Apache, caching headers are often set through mod_expires and mod_headers rules in the configuration or .htaccess file. On Nginx, the expires and add_header directives inside location blocks do the job. Managed platforms and CDNs usually expose the same settings in their dashboards. Start with static assets, confirm headers in DevTools, then review HTML and private pages separately.
Hosting plans differ in how much of this you can control. Our Linux hosting supports custom rules, and for tailored server configuration a VPS plan gives full access.
Frequently asked questions
What is the difference between browser cache and server cache?
Browser cache stores files on the visitor’s device to avoid repeat downloads. Server-side caching stores generated pages or query results on your server so it can respond faster. They complement each other.
Does browser caching help first-time visitors?
Not directly, since they have nothing stored yet. It helps from their second page view onward and on return visits, which is where it saves most time.
Why do visitors still see my old website after an update?
Their browser is likely reusing files that were given a long max-age without versioned names. Shorter lifetimes for HTML and versioned file names for assets prevent this.
Is it safe to cache pages with forms?
Public forms can be cached, but pages containing security tokens tied to a session, like many login or checkout forms, should not be shared from cache. Mark those private or no-store.
We can do this for you
See what a package covers and what it costs, or ask us about your own project.

