The Three Layers of WordPress Caching: Page Cache, Object Cache, and OPcache
“I cleared the cache and it’s still wrong.” “I installed a caching plugin, but the admin screen doesn’t change.” These are common questions in WordPress maintenance, and the cause is often that several different layers are being treated as one thing called “the cache.”
Around a WordPress site there are at least three distinct caches: the page cache, the object cache, and OPcache. They differ in what they store, where it lives, and how you clear it. This article sorts out the three and how to tell them apart. It describes general WordPress and PHP behavior, not any specific product.
Note: The transient API and Cache-Control / ETag are covered in separate articles. This one stays on the three server-side layers.
The big picture
Following a request from arrival to display, roughly from top to bottom:
| Layer | What it stores | Where | What it saves |
|---|---|---|---|
| Page cache | The finished HTML of a page | Disk or memory | PHP can be skipped almost entirely |
| Object cache | Results of database queries and option values | Memory (per request by default) | PHP runs, but fewer DB queries |
| OPcache | Compiled PHP code | PHP’s shared memory | PHP files are not re-read and re-parsed |
The key point: the higher the layer, the earlier it ends the work. A page cache hit barely touches PHP or the database. An object cache hit still runs PHP but reduces DB queries. OPcache doesn’t change whether PHP runs; it only avoids re-parsing PHP source each time.
1. Page cache: saving the finished HTML
A page cache stores the HTML WordPress has already assembled and serves it for the next request to the same URL. If logged-out visitors read the same post, there’s no need to run the theme and plugins and query the database every time.
It can live in several places:
- A caching plugin writes HTML files to disk and the web server serves them directly
- The web server side (reverse proxy, FastCGI cache, and so on) holds them
- A mechanism provided by the hosting company holds them
Three properties worth remembering:
- It isn’t suited to pages that differ per visitor. Logged-in screens, carts, and form results are normally excluded.
- Stale content can remain. After you update a post, the old HTML is served until the cache entry is discarded. Many plugins purge the affected page on update, but a theme or widget change may need a full purge.
- The admin area is generally excluded. If the admin screen looks stale, the page cache is often not the cause.
2. Object cache: reusing the database’s answers
A single page view makes WordPress fetch many values: the site name, theme settings, post lists, user data. Many live in the database, and querying each time is slow.
WordPress core therefore includes a built-in in-memory store (WP_Object_Cache) that reuses fetched values. Functions like wp_cache_get() and wp_cache_set() are its entry points.
However, by default this cache disappears when the request ends. It only helps within a single page view; the next request queries the database again.
A persistent object cache keeps values across requests. You provide an external in-memory store such as Redis or Memcached and connect it through the wp-content/object-cache.php drop-in. Then later requests also make fewer DB queries.
How to tell, and what to watch for:
- Check for
wp-content/object-cache.php, or runwp cache typein WP-CLI - Because values live outside the database, editing the database directly may not show on screen while the cache still holds the old value
wp cache flushclears it, but if several sites share one store, it can affect the others. Check first in shared environments.
3. OPcache: saving the PHP code itself
The first two cache data that WordPress produces or fetches. OPcache is different: it caches the PHP code itself.
PHP source is converted from text into internal instructions (opcodes) on each run. OPcache keeps the result in shared memory and skips the conversion next time. For an application with many PHP files like WordPress, this matters. It ships as a standard PHP extension and is enabled by default on many shared hosts.
The trap for maintainers: editing a file may not take effect immediately. OPcache has settings for how often it checks file modification times (such as opcache.revalidate_freq), and old code can keep running in between. In environments with timestamp checking turned off (opcache.validate_timestamps=0), old code runs until PHP is restarted or the cache is reset.
If you replaced a plugin or theme PHP file and behavior didn’t change, suspect this layer. You reset it by reloading PHP-FPM or using a reset function in the hosting control panel. Note that WP-CLI’s wp cache flush does not clear OPcache. That command operates on the object cache, a different layer.
When “I cleared it and it’s still wrong”
By symptom, which layer to suspect first:
| Symptom | Suspect first |
|---|---|
| Visitors see old posts or design | Page cache (and the browser’s own cache) |
| Admin or settings stay old after a direct DB edit | Object cache |
| A replaced PHP file behaves the same | OPcache |
| Only one external API’s result is stale | Transients (see the separate article) |
The basic method is to remove one layer at a time. Check while logged in (which bypasses the page cache), then temporarily disable the persistent object cache, and if nothing changes, suspect OPcache. Clearing everything at once leaves you unsure which layer was the cause.
The risk of clearing all three at once
“Just clear everything” is quick, but sometimes best avoided:
- A full page-cache purge sends the next requests to PHP and the database at once. In a busy period, load rises briefly.
- Flushing a shared object cache store can affect other sites.
- Right after an OPcache reset, PHP files are recompiled, so responses may be briefly slower.
Know which layer an action targets, do only what’s needed, and do it at a quiet time.
Summary
- The page cache stores finished HTML, the object cache stores values fetched from the database, and OPcache stores PHP code. What they store and how you clear them differ.
- The default object cache lasts only within a request. Persisting across requests needs something like Redis plus an
object-cache.phpdrop-in. - When a replaced PHP file doesn’t take effect, suspect OPcache, and remember
wp cache flushdoesn’t clear it. - Narrowing down by symptom and checking one layer at a time works better than clearing everything, both for finding the cause and for managing load.
Knowing which caches are active before and after an update makes post-update checks less confusing. The idea of taking a backup before updating is covered in the 3-2-1 rule article.