A cache is a stored copy of something that was expensive to produce, kept so the next request can be answered instantly. Websites use several layers of cache, each saving a different kind of work.
| Layer | Stores | Saves |
|---|---|---|
| Browser cache | Images, styles and scripts on the visitor’s device | Downloading files again |
| CDN | Copies of files or pages near the visitor | Distance and load on your server |
| Page cache | Finished HTML pages on your server | Running PHP and database queries |
| Object cache | Results of database queries, in memory | Repeating the same query |
| Opcode cache | Compiled PHP code | Re-reading and compiling PHP files |
Caching means keeping a ready-made copy of something so it does not have to be fetched or built again. On a website, caches exist at several layers, from the visitor’s browser to your server’s memory. A page cache alone often lets a small server handle many times more traffic.
Browser cache
Your server tells browsers how long to keep files with headers like Cache-Control: max-age=2592000 (30 days). Repeat visitors then load your images and styles from their own disk. Change a file’s name or add a version, such as style.css?ver=2, when you update it, so browsers fetch the new one.
Page cache
The biggest win for most sites. The first visitor to a page triggers the full work; the result is saved as a static file, and later visitors get that file. On WordPress this is done by a caching plugin or by the web server (for example Nginx’s FastCGI cache). See how much RAM a site needs for why this matters.
Page caching with Nginx
Without a plugin, Nginx can cache PHP pages itself. Define a cache in the http block:
fastcgi_cache_path /var/cache/nginx/site levels=1:2 keys_zone=SITE:32m max_size=512m inactive=24h;
fastcgi_cache_key "$scheme$host$request_method$request_uri";
Then, in the site, skip the cache for logged-in visitors and form posts, and use it for everyone else:
set $skip 0;
if ($request_method = POST) { set $skip 1; }
if ($http_cookie ~* "wordpress_logged_in|woocommerce_items_in_cart|comment_author") { set $skip 1; }
if ($request_uri ~* "^/(wp-admin|wp-login\.php|cart|checkout|my-account)") { set $skip 1; }
location ~ \.php$ {
# ...the usual fastcgi lines
fastcgi_cache SITE;
fastcgi_cache_valid 200 301 1h;
fastcgi_cache_bypass $skip;
fastcgi_no_cache $skip;
add_header X-Cache $upstream_cache_status;
}
The X-Cache header shows HIT, MISS or BYPASS, so you can check it is working with curl -sI https://example.com/ | grep -i x-cache. Clear the cache after publishing by deleting the cache folder’s contents, or set a short lifetime.
Object cache
A store in memory, such as Redis or Memcached, that keeps the results of database queries. It helps pages that cannot be fully cached, like shop carts and account pages.
To use Redis with WordPress, install the Redis server and the PHP extension, then a Redis object cache plugin:
sudo apt install redis-server php-redis
sudo systemctl restart php8.3-fpm
Object caching helps most on sites with many logged-in users, large shops and slow admin pages. On a small brochure site, a page cache does far more.
Opcode cache
PHP’s OPcache keeps compiled code in memory. It is built into PHP and should always be on.
Check it is on, and whether it has enough memory:
php -i | grep -E "opcache.enable |opcache.memory_consumption"
That shows the command-line PHP. The web version can differ; a phpinfo() page (removed straight afterwards) shows the PHP-FPM settings. On sites with many plugins, raising opcache.memory_consumption to 256 and opcache.max_accelerated_files to 20000 avoids recompiling.
When caching causes problems
- You see an old version after an edit: clear the page cache or the CDN.
- One visitor sees another’s data: a page meant for logged-in users was cached. Never page-cache carts, checkouts or account pages.
- Changes do not show for everyone: different layers expire at different times. Clear from the top down: page cache, then CDN, then browser.
How to tell if caching is working
| Test | Good sign |
|---|---|
Run curl -sI https://example.com/ twice and read the cache header | HIT from your page cache (x-cache) or CDN (cf-cache-status) on the second request |
Time to first byte: curl -o /dev/null -s -w "%{time_starttransfer}\n" https://example.com/ | Under 0.2 seconds on the server’s own network for a cached page |
| Browser developer tools, Network tab, second visit | Static files show “(disk cache)” or “(memory cache)” |
Related
- What a CDN is.
- How much RAM a website needs.
- 504 Gateway Timeout, often fixed by caching.
Something out of date? Software changes. If a step no longer works, tell us and we will check it and update the page.

