Skip to main content

WordPress speed: 10 ways to make your site faster

WordPress · 24.01.2025 · 6 min read
Illustration for “WordPress speed: 10 ways to make your site faster”

Why WordPress speed matters

Google uses load speed as a ranking factor. Every 100 ms of delay cuts conversion by 1%. Below are 10 concrete steps.

10 ways to speed up WordPress

#MethodSpeed gainDifficulty
1Page cache (LiteSpeed/WP Rocket)★★★★★Low
2CDN (Cloudflare / BunnyCDN)★★★★☆Low
3WebP images★★★★☆Low
4Image lazy loading★★★☆☆Low
5Redis Object Cache★★★★★Medium
6CSS/JS minification★★★☆☆Low
7Deferred JS (defer)★★★☆☆Low
8Database tuning (WP-Sweep)★★☆☆☆Low
9PHP 8.2 + OPcache★★★★☆Medium
10GZIP/Brotli compression★★★☆☆Low

Enabling OPcache in php.ini

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=0
opcache.validate_timestamps=0  ; production only!
💡 Use PageSpeed Insights and GTmetrix to measure Core Web Vitals before and after the optimization.

GZIP in Nginx

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 256;
gzip_comp_level 6;

Common errors when speeding things up

Turning on aggressive caching and PHP tuning sometimes breaks the site instead of speeding it up. Here is what specific error texts mean and how to fix them.

Error text or symptomCauseWhat to do
White screen after setting opcache.validate_timestamps=0OPcache keeps the bytecode of the old file version in memory and does not see the new deploymentCall opcache_reset() or restart php-fpm after every deployment
There has been a critical error on this websiteThe JS minifier merged incompatible theme and cache-plugin scripts into one fileDisable JS minification for the conflicting file only and check debug.log
Error establishing a database connection after enabling object cacheThe Redis or Memcached extension is unavailable on PHP-FPM, not the database itselfCheck php -m | grep redis and the status with systemctl status redis-server
Layout "jumps" after Lazy LoadingThe browser cannot reserve space for an image without width and height attributesSet real width and height on img tags, not just CSS sizes

For details on setting up Redis itself for WordPress, see the article on Redis object cache.

Limits: when cache and a CDN will not help

Cache-based speedups do not work for every page type or on every hosting plan.

  • Pages with personal content — account area, cart, checkout form — cannot be cached as a whole: every user would see someone else's data
  • On plans with a hard CPU limit (most shared hosting) you cannot run a Redis object cache — there is no access to systemd or your own services
  • If a site gets fewer than 100 visitors a day, setting up a CDN will not pay off the time spent — the bottleneck is usually in the code, not in static asset delivery
  • Sites with active WebSocket connections or AJAX long-polling do not get faster from page caching, because every request is unique
  • Heavy PDF catalogs and on-the-fly report generators will still hit the CPU on the first request; caching only hides the problem for repeat visits

Before turning on all ten methods at once, measure a baseline: without a baseline TTFB and Core Web Vitals number you cannot prove that a specific change actually helped rather than coinciding with a quieter time of day on the host.

Resource priority and Critical CSS

After the ten basic steps, the next level is moving the critical styles into head and loading the rest of the CSS asynchronously. This directly affects Largest Contentful Paint.

<!-- In head: inline critical styles -->
<style>/* only above-the-fold styles */</style>

<!-- The rest of the CSS — async -->
<link rel="preload" href="/wp-content/themes/mytheme/style.css" as="style" onload="this.rel='stylesheet'">
⚠️ Generating Critical CSS through a headless browser in some plugins can exceed fastcgi_read_timeout and trigger a 504 Gateway Timeout on weaker VPS instances — precompute the CSS in CI, not on every production deployment.

How to check that the optimization worked

PageSpeed Insights numbers vary from run to run, so verify with several methods at once.

# Check time to first byte
curl -o /dev/null -s -w 'TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n' https://example.com/

# Check that compression is served
curl -sI -H 'Accept-Encoding: gzip, br' https://example.com/ | grep -i content-encoding
  • Content-Encoding in the response is br or gzip, otherwise compression was not applied
  • An X-Cache: HIT header, or the equivalent from your cache plugin, on the second request for the same page
  • PageSpeed Insights: Largest Contentful Paint under 2.5 seconds on mobile
  • GTmetrix: TTFB under 200 ms on hosting with NVMe and PHP OPcache

If PHP is still the bottleneck after all these steps, check the OPcache settings for PHP, and check compression at the web server level in the article on GZIP and Brotli.

Frequently asked questions

Can cart and account pages be cached in WooCommerce?

No, pages with personal content — the cart, account area, checkout — can't be cached like ordinary static pages, since one shopper would end up seeing another shopper's data. Caching plugins usually exclude such URLs by default, but it's worth checking the settings manually to confirm that the store's dynamic sections are actually flagged as exceptions in the caching rules.

Why can a site show a white screen specifically after turning off opcache.validate_timestamps?

With validate_timestamps disabled, OPcache stops comparing files against disk and keeps the old version's bytecode in memory even after new files are deployed. If the new code isn't compatible with that stale cached state, the server returns an error instead of a page. The fix is resetting the cache or restarting PHP-FPM right after every file update in production.

Is setting up a CDN or Redis worth it for a site with very little traffic?

For a site with very few daily visitors, setting up a CDN usually doesn't pay back the time spent — the bottleneck is more often the code itself than static asset delivery. Redis isn't always an option either: on plans with a hard CPU limit there's simply nowhere to run an object cache, since there's no access to background services.

Why can the layout "jump" after turning on lazy loading for images?

The browser only reserves space for a picture ahead of time when the img tag has explicit width and height attributes. If only CSS sizing is set, or the attributes are missing, the browser doesn't know the image's proportions until it loads, so nearby content shifts right after it appears — felt as a sudden layout "jump."

Was this article helpful?
← Back to Knowledge Base Ask Support