WordPress performance optimization has a correct order of operations. Most guides start with image compression. That's wrong. You can have perfectly optimized images on a slow server and still fail Core Web Vitals. Here's the sequence that actually works.
Step 1: Fix the Foundation (Hosting)
Before touching a single plugin, check your TTFB. Open DevTools (F12 → Network → reload → click the first HTML request). Look at "Waiting (TTFB)" in the timing breakdown.
If your TTFB is over 600ms: No optimization technique will reliably get you to LCP < 2.5s. You need better hosting or a different server configuration.
Benchmarks by hosting tier:
| Hosting type | Typical TTFB (uncached) | Can hit LCP < 2.5s? |
|---|---|---|
| Managed (Kinsta, WP Engine) | 300–400ms | Yes, easily |
| Cloud VPS managed (Cloudways) | 350–450ms | Yes, with good caching |
| Quality shared (SiteGround) | 400–500ms | Yes, with aggressive caching |
| Budget shared (DreamHost) | 450–700ms | Marginal |
| Truly cheap shared | 600ms–2s | No |
If you're on budget shared hosting and hitting 700ms+ TTFB, move hosts first. Cloudways' $14/mo DigitalOcean plan will cut your TTFB in half versus a typical shared host.
Step 2: Configure Server-Level Caching
WordPress is dynamically generated by default — every page request hits PHP and MySQL. Server-level caching serves pre-built HTML files and eliminates that overhead.
Options by hosting:
- Kinsta: Uses Nginx FastCGI cache automatically. Zero configuration needed.
- Cloudways: Varnish cache available. Enable it in the Application Management panel.
- Self-managed VPS: Install Redis or Nginx FastCGI cache manually. Use our Script Generator to get a working Nginx cache config.
Do not install a caching plugin (W3 Total Cache, WP Rocket) as a substitute for server-level caching. Use it as a complement.
Step 3: Install WP Rocket (or Equivalent)
WP Rocket handles client-side optimization that server caching can't: CSS/JS minification, lazy loading, preloading, and database cleanup.
Configuration that matters:
- File Optimization → Minify CSS/JS: Enable both. Test after enabling — some plugins break on minification.
- Media → Lazy Load Images: Enable. This alone can knock 1–2 seconds off LCP on image-heavy pages.
- Preload → Preload Cache: Enable. Warms cache after it's cleared.
- Database → Optimize Tables: Run monthly. A WooCommerce store can accumulate thousands of orphaned rows.
Step 4: Fix Images (The Right Way)
Image optimization is step 4, not step 1. Once it's your turn:
Serve WebP: Convert all images to WebP. In WordPress 6.1+, WebP conversion is built into the media library. Enable it in Settings → Media.
Set explicit dimensions: Every <img> tag should have width and height attributes. Without them, the browser can't reserve layout space, causing CLS (Cumulative Layout Shift) — a Core Web Vitals failure.
Preload your LCP image: The LCP element (usually the hero image) should be preloaded. In your theme's functions.php or via a plugin:
function preload_hero_image() {
echo '<link rel="preload" as="image" href="/path/to/hero.webp" fetchpriority="high">';
}
add_action('wp_head', 'preload_hero_image', 1);
Serve images from a CDN: Kinsta includes Cloudflare CDN free. Cloudways has BunnyCDN as a paid add-on. For self-managed, use Cloudflare's free tier.
Step 5: Audit and Remove Render-Blocking Resources
Open Chrome DevTools → Lighthouse → run the audit → look for "Eliminate render-blocking resources."
Common culprits:
- Google Fonts loaded in
<head>(swap tofont-display: swapor preconnect) - jQuery plugins loaded site-wide but used on one page
- Unused CSS from page builders (Elementor loads its full CSS on every page)
For Elementor: Install the Elementor Optimizer or use the built-in "Improved Asset Loading" experimental feature in Elementor → Settings → Experiments.
Step 6: Database Optimization
WooCommerce in particular creates database bloat: expired transients, draft orders, old sessions, and revision overload.
Run this query monthly (or automate via WP Rocket's database optimizer):
-- Delete expired transients
DELETE FROM wp_options
WHERE option_name LIKE '%_transient_%'
AND option_name NOT LIKE '%_transient_timeout_%';
-- Delete auto-draft posts older than 30 days
DELETE FROM wp_posts
WHERE post_status = 'auto-draft'
AND post_modified < DATE_SUB(NOW(), INTERVAL 30 DAY);
After deleting, run OPTIMIZE TABLE wp_options; to reclaim disk space.
Measuring Your Results
Before and after every change, measure:
- Google PageSpeed Insights — tests from Google's servers (Lighthouse 10)
- WebPageTest — filmstrip view shows exactly what loads when
- Chrome DevTools → Performance — flame graph for JavaScript execution time
Target metrics for 2026:
- LCP < 2.5s (good) / < 1.2s (excellent)
- INP < 200ms (good) / < 100ms (excellent)
- CLS < 0.1 (good)
- TTFB < 800ms (good) / < 200ms (excellent)
Quick Win Checklist
- Check current TTFB — if > 600ms, fix hosting first
- Enable server-level caching (FastCGI / Varnish / Redis)
- Install WP Rocket and configure minification + lazy load
- Convert images to WebP
- Preload LCP image with
fetchpriority="high" - Remove unused plugins (every inactive plugin still loads autoloaders)
- Schedule monthly database optimization
The correct order matters. Don't compress images on a 700ms TTFB server and wonder why you're still at LCP 3.8s.