What caching is, and its layers
A cache keeps the result of a piece of work so the same work is not done again next time. On this site, the same page at almost exactly the same byte count takes 23.5 ms from cache and 155.5 ms without it for its first byte to arrive.
- Lesson 6 of 10
- Beginner
- Free, no signup
A request falls through these layers and the first answer it finds wins
From the top, closest to the visitor. The further down you go, the more expensive answering gets.
- Browser cacheon the visitor own device; no request is sent at all one year
- CDN edgeoutside your server; a server purge does not reach it thirty days
- Page cachefinished HTML on disk; when it exists, PHP never runs one file
- Object cacheanswers to questions inside the code; the only cache that works for a logged-in user in memory
- Code and databasethe foundation; here the page is genuinely built and you pay for it no cache
This order is for a guest visitor. A logged-in user skips the top three layers and lands straight on the bottom two.
Last checked: Facts and tool names in this lesson are re-checked against their sources on this date.
What does a cache actually store?
A cache keeps the result of a piece of work so that the work is not repeated next time. That definition is simpler than it sounds and it carries one consequence most tutorials walk past: a cache does not make your page lighter, it only makes it arrive sooner.
We took the number for that claim off this site. We requested the front page ten times from its cached copy and ten times with a cache-busting parameter, each time with compression on, and recorded when the first byte arrived. From cache the median was 23.5 ms, with a low and high of 22 and 37. Without cache the median was 155.5 ms, over a range of 134 to 182.
Now the size of those same two responses: 43.8 KB for the cached copy and 43.1 KB for the one built on the spot. The slow version is roughly the same size as the fast one, and in fact very slightly smaller. That whole six-fold gap is time, not bytes.
So when someone says caching makes a site fast, the truer sentence is this: caching removes the work the server was doing to build the page. That work means running code, asking the database, assembling the template and producing the HTML. What comes out is a file that gets sent directly next time.
The boundary of caching shows up in the same sentence, and it matters later: if your page is slow because it carries three megabytes of images, caching helps with none of it. A cache buys back build time, never download time.

The four layers a request falls through
When someone opens your address, the request passes through several caches and the first one holding an answer ends the journey. The order starts closest to the visitor.
The browser cache lives on the visitor own device. Files that do not change stay there, and next time no request is sent at all. On this site fonts, scripts and stylesheets go out with Cache-Control: max-age=31536000, which is one year. The main font is 25,272 bytes, and a visitor who came last week pays zero bytes for it today. This is the fastest layer because no network is involved.
The CDN edge sits outside your server and keeps a copy of the page near the visitor. That layer has a lesson of its own in the internet and networks path; what you need here is that the edge is usually steered by a separate header, not by the one you send the browser.
The page cache lives on your own server and keeps the finished HTML in a file. On this site those files really are files: the about page is one compressed file of 36,330 bytes, and the whole page cache is 1,980 files and 112 MB. When the file exists the web server sends it itself and PHP never runs at all, and that is what produces the 23.5 ms.
The object cache sits at the bottom of the stack and differs from the rest: it does not hold pages, it holds the answers to questions asked inside the code. On this site Redis 7.4.7 does that. Its value shows when the three layers above have been skipped, for instance for a logged-in user who never receives a cached page. For that person it is the only cache still working.
Below those four layers there is no cache at all: code runs and the database answers. Every time no layer holds an answer, the bill is that same 155.5 ms.
Page cache and object cache do different jobs
Page cache
- keeps the whole finished HTML in one file
- when it answers, PHP does not run at all
- for guest visitors; usually off for a logged-in user
Object cache
- keeps the answers to questions inside the code in memory
- PHP still runs but asks the database less
- the only layer left for the admin panel and a logged-in user
Neither replaces the other. A site with only the first has a slow panel, and a site with only the second has slow public pages.
Why has your change still not reached anyone?
This is the most everyday question in caching and the answer is nearly always the same: a layer in front of the one you cleared still holds the old copy.
The first thing to see is that one response can carry two different cache instructions, one for the browser and one for the edge. Pages on this site arrive with these two headers:
cache-control: no-cache, must-revalidate cdn-cache-control: max-age=2592000
So we tell the browser to ask every time, and we tell the edge to hold it for thirty days. That is deliberate and common, but its meaning has to land: clearing the cache on your own server does not touch those thirty days. You rebuild the page on the server, you feel finished, and the visitor keeps seeing whatever the edge is holding. Reaching the edge means asking the edge service to purge, and that is a separate act.
The second trap is smaller and costs more time, because it looks like a failed purge. So it does not go to disk on every request, the web server keeps the result of its file lookups for a short while; on this server that window is sixty seconds, set by open_file_cache_valid. So you delete the cache file, fetch the page immediately, and the old page arrives. Nothing is broken. Wait out the minute and fetch again.
And a third case people think about less: the visitor themselves. If you sent a file with a one-year lifetime and then changed its contents, the browser of anyone who came before will never find out. The right move is not changing the file contents, it is changing its name or the version in its address, so that a new address means a new file.
What should never be cached?
One sentence prevents every serious caching disaster: cache the page, not the person. Anything whose answer differs between two people must not be stored in a file that is then handed to both of them.
The examples are always the same ones: the shopping cart, the account page, any page that prints the user name, forms mid-completion, and payment result pages. The worst failure mode is exactly the one everybody fears: the first user sees their cart, it gets cached, and the second user sees the first user cart.
This is why a page cache usually does not work at all for a logged-in user, and that is a design decision rather than a defect. But you have to accept what follows from it: your admin panel is always slower than the public pages of the site, because it is genuinely built every time. That is precisely where the object cache becomes the only remaining tool.
There is also a borderline case you have to decide yourself: pages that are the same for everyone but go stale quickly, such as a page showing stock levels. These can be cached, provided the lifetime is short and you know what the worst case is. If the worst case is that somebody orders a thing that ran out, your lifetime needs to be very short or that fragment needs to stay outside the cache.
Cache these
- pages that are the same for everyone: articles, service pages, product pages
- files that do not change: fonts, images, versioned stylesheets and scripts
- pages that go stale but are the same for everyone, with a short lifetime
Never cache these
- the cart, the checkout and the payment result page
- any page printing a user name or their data
- the admin panel and any route only visible after login
- form responses and anything that changes with a single request
Purging is not free
The purge everything button is the simplest button in any cache plugin and the most expensive thing you can do with a cache. We take a position here, because we have paid for it.
When you clear the whole cache, every page that was dropped has to be rebuilt. If your plugin preloads pages after a purge, that rebuilding starts immediately rather than when a visitor arrives. On a site with 1,980 cached pages that means close to two thousand full PHP runs back to back. With that median of 155.5 ms, do the multiplication yourself, and remember it happens on a server that is answering real visitors at the same moment.
The rule we actually work by is simple: purge only the pages that genuinely changed. If you edited the text of one page, that is one address. If the site is multilingual, each language is a separate address and counts separately. A site-wide purge is justified only when something changed that really is on every page, such as the header or a global stylesheet, and even then it belongs in a quiet hour.
And the order of operations, which sounds obvious and is usually done backwards: check the change on the uncached version first and satisfy yourself it is right, then purge. Do it the other way and every save is another purge, and the same page gets rebuilt five times.
The fast path, with AI
The usual way, when a change is not reaching the site, is to clear everything one by one until it finally works. It does work and it is expensive every time. The fast path is to ask first which layer answered, and the response headers say so. A language model does this well, because the job is reading structured text and relating a few lines to each other. The thing that separates this from an ordinary question: take the headers from two addresses, the address itself and the same address with a meaningless parameter, and hand over both. The difference between those two is itself the diagnosis.
- Take the headers of both cases: once the address itself, once the same address with a random parameter such as ?cb=12345 that no cache has ever seen.
- Hand both to the model together and ask which layer answered and which header shows it. Leaving out the second case is the mistake that makes the answer useless.
- Ask it to name only the layer at fault and propose the smallest necessary action, and say explicitly that it must not propose a site-wide purge.
- Do the proposed action, wait a minute and take the headers again. The wait is not optional: the web server sixty-second window is exactly what makes a correct action look like a failure.
Copy-ready recipe
These are the response headers of a page on my site, in two cases.
Case A, the normal address:
{paste the full headers here}
Case B, the same address with a random parameter no cache has seen:
{paste the full headers here}
I want to know why a change I made on the server is still not visible.
Rules:
1. Reason only from these headers. If a header you need is not in the text, say which one is needed. Do not guess.
2. Say which layer answered in case A: browser, edge, page cache or code. And say which line shows it.
3. If case A and case B differ in content, say what that means.
4. Name one layer at fault, not several.
5. Propose the smallest necessary action. Do not propose a site-wide purge or clearing all cache.
6. Do not name any setting that is absent from these headers.
Answer in four lines: the layer that answered, the reason, the proposed action, what should have changed afterwards.
Before you trust the output: The model reads headers well and guesses settings badly. Ask it without headers why your site is not updating and it will confidently write the names of options that do not exist in your panel, and you will spend half an hour looking for them. Rule six above exists for that. And one job never goes to the model: the decision to purge everything. That is an operational call that depends on how many pages you have and what hour it is, and the model knows neither.
AI in this kind of work
Caching is a place where AI is very good at one job and dangerous at another. It is good at reading headers, explaining a directive and translating an error message into human language. It is dangerous at proposing settings, because every cache plugin differs and the model has no way of seeing your panel. Our position: use the model for diagnosis, not for configuration.
Tools that actually help
- Claude Suited to the diagnostic job above: it reads a structured text, finds the relations between a few lines, and holds to rules if you set them. Iran is not on the Anthropic supported-countries list, so there is no official signup and no Iranian card is accepted.
- Gemini Good for when you want to understand a term or a directive quickly, the difference between no-cache and no-store for instance. Google own page says the Gemini web app runs in over 230 countries and territories, and Iran is not on that list.
- ChatGPT Enough for the same header reading and explaining. We make no claim about access from Iran, because the OpenAI supported-countries page, like the rest of that domain, returns 403 to this server.
Where it backfires
The main risk here is invented names, and it has a shape worth recognising. The model has seen the pattern of cache headers and directives, so it can write a header that does not exist, or the name of an option in a plugin panel that never had such an option. Because its answer looks like a correct answer, spotting it is hard. The simple test is this: any name the model wrote that you cannot see in your own real headers or on your own settings screen does not exist until proven otherwise. The second risk is operational: ask almost any model why your change is not showing and somewhere in the answer it will suggest clearing the entire cache. On a small site that is harmless and on a site with thousands of pages it is expensive, and the model does not know which one yours is.
Sources: MDN: Cache-Control Anthropic: supported countries Google: where Gemini Apps are available
Where this advice stops
This lesson is about caching the pages of an ordinary site and it has three boundaries worth knowing. First, caching inside the code itself, where a developer keeps the result of an expensive computation in memory, is a different subject and we did not go into it here. Second, the numbers on this page come from one server in Europe with a warm disk; the ratios generalise to your site, the numbers do not, and the only number worth trusting is one you measured on your own site. Third and more important than either: if your site genuinely builds a different page for every visitor, like a dashboard or a social network, a page cache is not the right tool for you and your answer lies lower in the stack.
From our own work
The most expensive hour we have ever spent on caching went into chasing a bug that was not a bug. We deleted the cache file of one page, fetched the page immediately, and the old copy arrived. We deleted it again, same result. We went looking for whatever was putting the file back. The answer was that the web server, so it does not go to disk on every request, keeps the result of its file lookups for sixty seconds, and we were retesting inside that same sixty-second window every time. Nothing was broken; we had not waited. That is now a fixed rule for us: after purging one page we wait a minute and then check, and if something looks wrong immediately after a purge we look at the clock before we look at the code.
Real follow-up questions
How many cache plugins do I need?
One. Two page-cache plugins on one site fight each other, and the result is pages that sometimes update and sometimes do not, with the culprit almost impossible to find. An object cache is a separate plugin and does not conflict with a page cache; that one you can run alongside.
If I have a CDN, do I still need a page cache?
Yes, because they fill different gaps. The edge only holds pages someone already requested whose lifetime has not run out; for any address that is new or has fallen out of the edge, the request reaches your server, and there the page cache decides whether PHP runs. A site with only an edge tastes the full slowness every time the edge is empty.
What lifetime should I set for the cache?
Decide with a question rather than a default number: if this page were an hour old, what would go wrong? For an article the answer is nothing, so set a long lifetime. For a page showing a price or stock level the answer is a customer complaint, so either make the lifetime very short or keep that fragment out of the cache. Files with a version in their name are the exception and can stay a year, because changing them means a new name.