The received answer is the database, and it is usually wrong.

A well indexed query for one post takes under a millisecond. If that were the bottleneck, the page would be fast. Sit with a profiler on a typical install and the time is somewhere else entirely.

Bootstrap. Before a single line of your page runs, the application has loaded its core, enumerated the plugin directory, loaded each plugin, and registered several thousand hooks. That work happens on every request and produces nothing a visitor sees. On a stock WordPress with twenty plugins it is comfortably the largest single cost.

Query multiplication. Not one query, ninety. Each in itself trivially fast. The page asks for a post, then its author, then its terms, then options, then something in a widget, then an image size for a thumbnail. Nobody wrote ninety queries. The template asked ninety questions, and each answer was a query nobody could see from where they were standing.

Rendering. Templates producing HTML, filters modifying it on the way out. Cheap individually.

And everything that happens after the HTML. Twelve stylesheets, a jQuery build, three tracking scripts, fonts, and images at the wrong resolution. For the actual visitor experience this frequently dwarfs everything the server did.

The industry response was caching, at four levels: an object cache to stop the query multiplication, a page cache to skip the application, a CDN to skip the origin, and a browser cache to skip the request. Each is a correct optimisation for the layer below it and each adds a way for a change not to appear when someone publishes.

That last part is the real cost and it is cultural rather than technical. In an organisation running four cache layers, "the site is slow" and "my change is not showing" are both routine, and nobody can be certain which layer is responsible without checking all of them.

What I take from it is that the fix is upstream. Bootstrap cost is avoidable if the application does not rebuild its world every request. Query multiplication is avoidable if the read path fetches what a page needs in one pass rather than answering questions one at a time. Neither of those needs a cache, and both are much harder to add later than to design in.

There is an existence proof for this in a different corner of the industry. OpenResty runs Lua inside nginx, keeps state in the worker between requests, and serves a request without process startup at all. People run it in front of applications precisely to avoid the bootstrap cost of the application. Nobody much uses it to build the application itself, which strikes me as an odd gap given how much of what makes a CMS slow is exactly the thing it removes.

The budget worth holding is that a page assembled from content the server already has in memory should be sub millisecond, and if it is not, caching is treating a symptom.