Without a cache, every WordPress page view runs PHP: boot the core, load every active plugin, run thirty to a hundred database queries, render the theme. That is 300 to 900 milliseconds of server time for an ordinary site, and it happens again for the next visitor asking for the identical page. A full-page cache stores the finished HTML and serves it without touching PHP, which takes time to first byte down to somewhere between 20 and 80 milliseconds. Nothing else you can do is in the same league.
Everything after that is smaller and more specific: OPcache so PHP stops recompiling itself, an object cache for the requests a page cache cannot cover, image discipline so the browser downloads less, and front-end work that hosting cannot do for you at all. This post goes through them in the order that returns the most time, and tells you which measurement proves each one worked.
Where the time actually goes#
Before changing anything, split the page load into the part the server owns and the part the browser owns. curl gives you the first half for free:
$ curl -o /dev/null -s -w "dns %{time_namelookup} tcp %{time_connect} \tls %{time_appconnect} ttfb %{time_starttransfer} total %{time_total}\n" \ https://example.com/| Phase | Typical good value | What it means |
|---|---|---|
dns | 0.01-0.05 s | Resolver lookup, cached after the first request |
tcp | 0.02-0.10 s | Round trip to the server - distance, mostly |
tls | 0.04-0.15 s | Handshake, one or two more round trips |
ttfb | 0.05-0.20 s | Everything above plus the server thinking |
total | under 1 s | Plus downloading the HTML itself |
Subtract tls from ttfb and what remains is the server's own work: PHP, the database, and the file system. That number is the only one caching changes. If your ttfb is 0.6 s and tls finished at 0.12 s, you have roughly half a second of PHP to attack. If ttfb is 0.18 s and the page still feels slow, the problem is in the browser and no host will fix it - TTFB, Core Web Vitals and hosting draws that line in more detail.
Run the request twice. The second one tells you whether a cache is working; the first tells you what an unlucky visitor gets.
Page caching, which is most of the win#
A page cache keeps the rendered HTML for a URL and serves it directly on the next request. For a blog, a brochure site or a documentation site, that covers nearly every visitor, because nearly every visitor is logged out and asking for something someone else already asked for.
WordPress has a hook for this built in. Setting define( 'WP_CACHE', true ); in wp-config.php makes core load wp-content/advanced-cache.php very early, before most of the framework, and a caching plugin drops that file in. The popular ones are WP Super Cache, W3 Total Cache, Cache Enabler, LiteSpeed Cache (only useful on a LiteSpeed server) and WP Rocket. Pick one. Two page caches on one site is the classic way to serve a logged-in admin bar to the public.
Three settings matter more than the rest of the plugin's screens:
- What bypasses the cache. Any request carrying a
wordpress_logged_in_*,comment_author_*,wp-postpass_*or, in a shop, a cart cookie must be served fresh. Every serious plugin does this by default. The failure mode when it is wrong is not slowness, it is one user seeing another user's page. - Cache lifetime. A few hours to a day is right for most sites. Short lifetimes turn into a permanently cold cache on a low-traffic site, where each page expires before anyone asks for it twice.
- Preloading. Walking the sitemap to warm the cache after a purge. Worth having on a site with hundreds of pages and modest traffic; pointless on a busy one, where visitors warm it in a minute.
Verify it from outside, logged out, in a private window or with curl -I. Most plugins add a header - x-cache: HIT, x-litespeed-cache: hit, a comment at the bottom of the HTML. If you cannot find such a marker, compare ttfb on two consecutive requests to the same URL: a working page cache shows an obvious cliff.
What a page cache cannot help: the cart, the checkout, the account pages, a logged-in forum, anything personalised per visitor. On a shop, that means the pages that matter most to revenue are exactly the ones still running PHP on every hit, which is why WooCommerce hosting requirements are different in kind rather than in degree.
OPcache#
PHP compiles source into opcodes on every request unless OPcache is enabled to keep them in shared memory. WordPress with twenty plugins is thousands of files, so this is worth 30 to 50 per cent of the PHP time on an uncached request and it costs nothing but memory.
opcache.enable=1opcache.memory_consumption=192opcache.interned_strings_buffer=16opcache.max_accelerated_files=20000opcache.validate_timestamps=1opcache.revalidate_freq=2| Setting | Shipped default | Why change it |
|---|---|---|
opcache.memory_consumption | 128 (MB) | A plugin-heavy site can fill 128 MB; a full cache silently stops caching |
opcache.interned_strings_buffer | 8 (MB) | Raised with the above on large sites |
opcache.max_accelerated_files | 10000 | WordPress plus 30 plugins can pass 10,000 files |
opcache.validate_timestamps | 1 | Leave on for WordPress |
opcache.revalidate_freq | 2 (seconds) | How stale a changed file may be served |
The one to resist is opcache.validate_timestamps=0. It is the right setting for an application deployed from Git with a restart afterwards, and the wrong one for WordPress, where plugin and core updates change files from inside the admin. With timestamp checks off, those updates appear to do nothing until PHP is restarted, and a half-applied update is a broken site.
Check whether OPcache is full rather than assuming. opcache_get_status() in a one-line script, or the status page most caching plugins include, reports num_cached_keys against max_cached_keys, used_memory against free_memory, and a hit rate. A hit rate below about 95 per cent on a steady site means the cache is being evicted, which means one of the first two numbers is too small.
Object caching and the database#
WordPress has an internal object cache that stores query results and computed values under keys. By default it is per-request: it is built at the start of a request and thrown away at the end, so it only saves repeated work inside one page load. Making it persistent needs a drop-in at wp-content/object-cache.php backed by Redis or Memcached, installed by a plugin such as Redis Object Cache.
Be honest about whether that is available to you. It needs a Redis or Memcached server that your PHP process can reach, and the extension to talk to it. Plenty of shared and container-based web hosting has neither - RE:NODE's managed database lines are PostgreSQL and MongoDB, which WordPress does not use - so if you cannot point at a real instance, a persistent object cache is not on your list and a well-tuned page cache matters proportionately more. Redis, and when you actually need it is the decision in general terms.
What you can always do is stop the database being asked for so much in the first place. Start with autoloaded options, which are read in full on every single request:
SELECT option_name, LENGTH(option_value) AS bytesFROM wp_optionsWHERE autoload IN ('yes', 'on')ORDER BY bytes DESCLIMIT 20;The IN clause is there because WordPress 6.6 changed the autoload column from plain yes/no to a set that includes on, off and auto, so a query written for the old values silently returns nothing on a new install. Add up the bytes column: under a few hundred kilobytes is healthy, over a megabyte is a tax on every page view. The usual culprits are a plugin storing a large cached payload as an autoloaded option and years of orphaned transients left by plugins that were deleted without cleaning up.
Post revisions are the other easy win. A page edited eighty times carries eighty copies in wp_posts. Cap them in wp-config.php rather than deleting them by hand every year:
define( 'WP_POST_REVISIONS', 10 );define( 'EMPTY_TRASH_DAYS', 14 );Images, which are most of the bytes#
On a typical WordPress page, images are 60 to 80 per cent of the transferred weight. The server does not have to be involved to fix that.
- Upload sensible originals. WordPress scales anything wider than 2,560 pixels and keeps the original beside it as
-scaled, so a 6,000-pixel phone photo costs storage twice and buys nothing. Resize before uploading. - Use a modern format. WordPress has accepted WebP uploads since 5.8 and AVIF since 6.5. A WebP is commonly 25 to 35 per cent smaller than the equivalent JPEG at the same visual quality.
- Lazy loading is already on. Since 5.5 WordPress adds
loading="lazy"to images, and since 6.3 it marks the likely largest image withfetchpriority="high"instead, which is the correct behaviour for Largest Contentful Paint. A plugin that lazy-loads everything including the hero image makes LCP worse. - Regenerate thumbnails after a theme change. A new theme registers different sizes; without regeneration the browser is served the full-size file scaled down in CSS.
- Watch the storage. Every registered size multiplies each upload. Five sizes plus the original plus the scaled copy is seven files per photo, and a media library is the single biggest use of disk on most WordPress plans.
The front end is usually the slower half#
Once time to first byte is under 200 milliseconds, the remaining seconds belong to the browser, and that is where most "slow WordPress" actually lives. Hosting does not change any of the following.
- Render-blocking CSS and JavaScript. A page builder that loads eight stylesheets and jQuery plus five plugin scripts before anything paints.
- Fonts. Three families in four weights, each a separate download; self-host them, subset them, and set
font-display: swap. - Third-party scripts. A chat widget, two analytics tags, a consent banner and a pixel. Each is a DNS lookup, a connection and a blocking script, and they are consistently the worst Interaction to Next Paint offenders.
- Sliders and hero videos. Large, above the fold, and usually decorative.
Measure this half with a browser tool, not with curl. The field data in a Core Web Vitals report will disagree with your lab test, and the field data is the one that counts. HTTP caching headers explained covers the other half of front-end speed: getting the browser to keep what it already downloaded, so repeat views cost nothing.
PHP workers, and the capacity nobody mentions#
Speed under load is a different quantity from speed when idle. A PHP-FPM pool has a fixed number of worker processes; each serves one request at a time. So the arithmetic is unforgiving:
requests per second = workers / average request secondsFour workers at 0.5 s per uncached request is eight requests per second, and the ninth visitor queues. Add a page cache and those same four workers serve only the small fraction of requests that miss it, while the rest are answered from disk in microseconds. That is the real reason caching matters on a small plan: it changes capacity by an order of magnitude, not just latency.
Worker count is bounded by memory. Each WordPress worker holds 40 to 80 MB resident, sometimes more with a heavy theme, so 1 GB of RAM supports perhaps eight to twelve workers once the web server and database have their share. Setting pm.max_children above what memory supports does not add capacity; it converts a queue into an out-of-memory event. How much RAM WordPress needs does this arithmetic properly, and the php.ini settings that matter covers memory_limit, which is the per-process side of the same question.
What hosting changes, and what it does not#
Hosting owns four things, and it is worth being precise about them, because a host cannot sell you a faster theme.
- CPU share sets how fast one PHP request runs. A hard CPU throttle means a server at its limit is slow, not broken.
- Memory sets how many requests run at once, through worker count.
- Disk matters less than people think once OPcache is warm, but NVMe shortens cold reads and makes database writes cheap.
- Distance sets the floor on
tcpandtls. A German server answering visitors in Brazil pays 200 milliseconds per round trip no matter what you cache. That is the case for a CDN in front, not a different host.
On RE:NODE, web plans run on NVMe storage on hardware in Germany, CPU is throttled to the share the plan bought rather than borrowed from neighbours, and the console shows the web server's own output so a 500 is visible without a support ticket. If a site does reach its memory limit the container is stopped and restarted clean instead of being left to swap, which is abrupt but keeps the machine predictable - one more reason to size workers to the plan rather than to optimism. Reading a server load graph explains what the panel's memory and CPU graphs are telling you before it comes to that.
FAQ#
Which caching plugin should I use?
Any of the main ones, configured once and left alone. WP Super Cache and Cache Enabler are the simplest, W3 Total Cache the most configurable and the easiest to misconfigure, LiteSpeed Cache the best choice only if the server actually runs LiteSpeed. The difference between two well-configured page caches is small; the difference between one and none is enormous.
Does a CDN replace a page cache?
No, they solve different halves. A CDN moves bytes closer to the visitor and can cache static files brilliantly, but unless you configure full-page caching at the edge it still asks your origin to build every HTML page. Run both: the page cache makes the origin cheap, the CDN makes the distance short.
Why is my site fast for me and slow for visitors?
Three reasons, in order of likelihood: you are logged in and skipping the cache, your browser has everything cached already, and you are geographically close to the server. Test logged out, in a private window, with a tool that requests from somewhere else.
Is object caching worth it without Redis?
There is no persistent object cache without Redis or Memcached, and database-backed substitutes generally move work rather than remove it. Spend the effort on the page cache, on trimming autoloaded options, and on removing the plugin that runs forty queries per page.
Will more RAM make my site faster?
Only if you are short of it. More memory buys more concurrent PHP workers, so it raises the point at which the site slows under load - it does not make a single page render faster. If one visitor at a time is slow, the answers are OPcache, fewer queries and a lighter theme.
How do I find the plugin that is slowing everything down?
Install Query Monitor, load a slow page logged in as an administrator, and read the query count, the slowest queries and the time spent per hook. It names the file and the plugin. Twenty queries per page is normal, two hundred is a plugin doing something foolish on every request.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.