Disclosure: we may earn a commission or referral reward from CloudCone, Contabo, DigitalOcean (account credit, not cash), Hostwinds, InterServer, RackNerd, VPSDime and Vultr if you sign up through our links. It does not change the price you pay. Every other provider pays us nothing. Prices in this guide are as of the date shown and may have changed; see the comparison pages for the latest checked prices.
Guides · BLOG
How to speed up WordPress on any VPS in 2026: the honest checklist
Some prices quoted in this guide are out of date. It is being rebuilt on our price index; for current prices use the comparison pages, which show when each price was checked.
WordPress is dominated by "39 plugins to speed up WordPress" listicles that recommend the same three affiliate plugins. This is the honest checklist — the specific changes that actually matter for TTFB, LCP, and real perceived speed. Ordered by impact.
The one-line answer
FastCGI cache + Redis object cache + Cloudflare + PHP 8.3 with OPcache tuned = a WordPress site that TTFB in 100-300ms on a $4.60 Hetzner VPS.
Everything else is 10-20% wins on top. This is 80% of the total.
Level 1: FastCGI page cache (biggest win)
Every uncached WordPress request runs PHP, hits MySQL 30+ times, renders HTML. Cached response: reads the file. 100x faster.
Nginx FastCGI cache config (excerpt of http {} block):
```nginx
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
```
In your server block:
```nginx
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
include snippets/fastcgi-php.conf;
# Cache anonymous GET requests only
set $skip_cache 0;
if ($request_method != GET) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~ "/wp-admin/|/xmlrpc.php|wp-..php|/feed/|index.php|sitemap(_index)?.xml") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { set $skip_cache 1; }
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
add_header X-Cache-Status $upstream_cache_status;
}
```
Reload Nginx: systemctl reload nginx.
Test: curl -I https://yourdomain.com/ — you should see X-Cache-Status: MISS first hit, HIT on the second.
Impact: TTFB drops from 500-1500ms to 30-80ms on cached pages. Biggest single win.
Level 2: Redis object cache
For pages that CAN'T be cached (logged-in, checkout, admin), you still want fast queries. WordPress queries wp_options (autoloaded), wp_posts, wp_postmeta heavily. Redis object cache stores results.
Install Redis:
```bash
apt install redis-server php-redis
systemctl enable --now redis-server
```
Install the WP plugin: search "Redis Object Cache" → activate → click "Enable Object Cache".
Impact: dynamic page requests drop from 300-800ms to 100-300ms. WooCommerce cart / checkout page speedups are dramatic.
For real production WooCommerce, consider Object Cache Pro ($95 one-time or via Cloudways at $10/mo) — better invalidation and support.
Level 3: Cloudflare in front
See our Cloudflare setup guide. Real CDN for static assets, DDoS protection, edge SSL.
With Cloudflare's cache-everything page rule on public pages, TTFB from the visitor's perspective drops to sub-100ms globally.
Impact: 50-100ms saved per request for users far from your VPS's region.
Level 4: PHP 8.3 + OPcache tuning
Update PHP to 8.3 (if not already):
```bash
apt install php8.3 php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip
update-alternatives --set php /usr/bin/php8.3
```
Tune OPcache in /etc/php/8.3/fpm/php.ini:
```ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0 ; only in production; requires cache flush on deploy
opcache.jit_buffer_size=100M
opcache.jit=1255
```
systemctl restart php8.3-fpm.
Impact: 15-30% faster PHP execution.
Level 5: Database tuning
Edit /etc/mysql/mariadb.conf.d/50-server.cnf (or Postgres equivalent):
For a 4 GB VPS running WordPress + MySQL:
```
innodb_buffer_pool_size = 1G
innodb_log_file_size = 128M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
```
systemctl restart mariadb.
Impact: 20-40% faster query response on write-heavy workloads.
Level 6: Image optimization
The biggest single thing eating LCP on most WP sites: unoptimized images.
Automatic image optimization plugins — I recommend EWWW Image Optimizer or Imagify. Both auto-compress on upload.
Or manually:
- WebP conversion for JPG/PNG.
- Responsive
srcset(WordPress does this automatically since 4.4). - Lazy loading (default in WordPress 5.5+).
Image CDN — if you use Cloudflare, their Polish feature (paid Pro tier) auto-optimizes images at the edge.
Level 7: Reduce plugin count
Every plugin adds queries, adds PHP execution, adds risk. Audit your plugins. For each:
- Do you actually use it?
- Is there a lighter alternative?
- Would code that does the same thing be less overhead?
Especially painful: "SEO" plugins (Yoast, RankMath) are heavy. Consider SEOPress Free or The SEO Framework — same features, lighter.
Level 8: Autoload cleanup
wp_options table accumulates autoloaded rows over time. Some are 500 KB each. Every request loads all autoloaded data.
Check autoloaded size:
```sql
SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload = 'yes';
```
If it's over 500 KB, you have bloat. Common culprits: transients that didn't expire, plugin data from deleted plugins, session data.
Clean up manually or with a plugin like Autoload Size Query.
Level 9: HTTP/2 and Brotli
Nginx supports HTTP/2 by default when SSL is configured. Verify:
```nginx
listen 443 ssl http2;
```
Enable Brotli compression (better than gzip):
```bash
apt install libnginx-mod-brotli
```
In /etc/nginx/nginx.conf:
```nginx
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
```
Impact: 15-25% smaller responses for supported browsers.
Level 10: Cron out of band
WP-Cron runs on the request path by default. Every request checks if any scheduled task is due. Move it to system cron:
In wp-config.php:
```php
define('DISABLE_WP_CRON', true);
```
Add to cron:
```
/5 * curl -s https://yourdomain.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
```
Impact: 10-20ms faster on every request.
What NOT to do
- Don't buy caching plugins that duplicate what FastCGI cache does. Rocket Loader, W3 Total Cache, WP Super Cache all add complexity and often hurt performance if you already have FastCGI cache.
- Don't enable Cloudflare's Auto Minify. It occasionally breaks WordPress. Use minification at build time if at all.
- Don't use "MU-Plugins" for random performance hacks from tutorials. Real gains come from architecture, not tweaks.
The measured result on a $4.60 Hetzner CX22
A WordPress site with 30 plugins, 500 posts, 100 pages after applying all 10 levels above:
- TTFB (cached): ~40ms (Cloudflare cache HIT).
- TTFB (origin): ~80ms (VPS FastCGI cache HIT).
- TTFB (uncached dynamic): ~250ms (Redis-cached WP query).
- LCP: ~1.2s over typical connection.
On a $4.60/mo Hetzner box. No CDN paid tier. No caching plugin.
Related guides
- How to add Cloudflare in front of your VPS — Level 3 in detail.
- Best hosting for a WordPress site — the hosting options.
- Best hosting for a WooCommerce store — ecommerce-specific.
- Managed WordPress to VPS + Nginx — the migration that produces this stack.