Why "install a cache plugin" is bad advice
Most WordPress performance advice you find online is the wrong order of operations. People tell you to install WP Rocket, switch to Cloudflare, optimize images. Those are all valuable β but if you do them before measuring, you may be hiding a structural problem under a thin layer of caching that breaks the moment cache misses occur.
Real performance work starts with a question: where is the time actually being spent? Until you answer that, every "optimization" is a guess.
Below is the methodology we apply on every paid performance audit. It takes about 90 minutes for an experienced engineer.
Step 1 β Establish a baseline
You cannot improve what you do not measure. Before changing anything, capture three numbers from real visitor sessions.
The three you need
- TTFB (Time to First Byte) β how long from request sent to first byte received. Target under 200ms for fast sites, under 600ms for acceptable.
- LCP (Largest Contentful Paint) β when the main hero/content becomes visible. Target under 2.5s.
- INP (Interaction to Next Paint) β the new responsiveness metric replacing FID. Target under 200ms.
How to capture them
PageSpeed Insights, WebPageTest, or Chrome DevTools Lighthouse give you a lab measurement. Real User Monitoring (RUM) β via Cloudflare Web Analytics, Vercel Speed Insights, or self-hosted Plausible β gives you field data from real visitors. Always trust field data over lab.
We run the audit URL through PSI three times in 5 minutes and average the results. Single measurements are noisy.
Step 2 β Identify which layer owns the bottleneck
WordPress slowness lives in one of four layers. Knowing which one saves you from optimizing the wrong thing.
| Layer | What it does | Bottleneck signs |
|---|---|---|
| Frontend | Browser rendering, JS, CSS, images | LCP high, FCP fine |
| CDN/proxy | Cloudflare, Fastly, your reverse proxy | TTFB high from far regions only |
| PHP | WordPress code execution | TTFB high uniformly, CPU pegged |
| MySQL | Database queries | TTFB high, slow queries in log |
The diagnostic: hit the same URL from the same network with curl -w "%{time_starttransfer}n" -o /dev/null -s. If TTFB is high, the issue is server-side. If TTFB is fine but the browser is slow, it's frontend.
Step 3 β Server-side analysis
If TTFB is the problem, dig into the server.
The first 5 commands we always run on the WordPress server
# Live CPU/memory
htop
# What's actively consuming I/O
iotop -o
# Recent CPU spikes
sar -u 5 12
# PHP-FPM worker pool status
curl http://localhost/fpm-status
# Open MySQL connections
mysql -e "SHOW PROCESSLIST" | wc -lThese five commands answer: is the server starved (CPU, RAM, IO), is PHP-FPM saturated, is MySQL backed up. Without this, every "WordPress optimization" is guesswork.
Red flags
- Sustained CPU above 80% across all cores β the bottleneck is PHP or MySQL workload
- IOwait above 15% β disk is the bottleneck (slow SSD, full disk, swapping)
- PHP-FPM workers at max β either traffic exceeds capacity or individual requests are slow
- MySQL connection count near
max_connectionsβ DB-side issue
Step 4 β PHP profiling
Once server resources look acceptable, drop into PHP-level profiling. Goal: find which plugin, theme function, or query consumes the most time per request.
Tools that give real answers
- Query Monitor (free plugin) β shows queries, hooks, http calls, scripts, styles. Always disable in production after auditing.
- New Relic APM β production-grade per-transaction profiling. Free tier covers small sites.
- Datadog APM or Atatus β alternatives if you prefer their pricing.
- Tideways β purpose-built for PHP profiling, less expensive than New Relic.
In our experience, 80% of "slow WordPress" cases come down to a single offending plugin or a single query that fires on every page. The profiler names it in under 5 minutes.
Common findings
- A "social share count" plugin making an outbound HTTP call to Facebook on every page load
- A "real-time visitor counter" running a full
wp_postmetascan per pageview - An SEO plugin generating sitemap on every request instead of caching
- A backup plugin running an
mtimewalk overwp-contentevery 5 minutes
Step 5 β Database analysis
If profiling points to MySQL, drop into the DB layer.
Three queries every WordPress DBA should know
-- Find autoloaded option bloat (the silent killer)
SELECT option_name, LENGTH(option_value) AS size_bytes
FROM wp_options WHERE autoload = 'yes'
ORDER BY size_bytes DESC LIMIT 20;
-- Identify queries scanning many rows
EXPLAIN SELECT * FROM wp_postmeta WHERE meta_key = 'some_key';
-- See current and historical slow queries
SHOW VARIABLES LIKE 'slow_query%';The autoload trap
If autoloaded options exceed 1MB, every page load is unnecessarily slow because WordPress loads them all into memory at bootstrap. Common offenders: expired transients, plugin "session" data, cached external API responses with no expiry.
Cleanup query:
DELETE FROM wp_options WHERE option_name LIKE '_transient_%' AND option_name NOT LIKE '_transient_timeout_%';(Always backup first.)
Adding indexes
WordPress's default schema doesn't include compound indexes on wp_postmeta. For sites with custom field heavy queries, adding (post_id, meta_key) cuts query time by 90%+. This is one of the highest ROI database changes you can make.
Step 6 β Frontend optimization
After the server returns the HTML fast, the browser still has to render it. Frontend bottlenecks dominate when TTFB is already fine.
The frontend checklist
- Hero image preloaded with
<link rel="preload" as="image"> - All images served as WebP or AVIF with appropriate sizing
- CSS critical-path inlined (top 14KB at minimum)
- JavaScript deferred or async (never blocking the parser)
- Third-party scripts (analytics, chat, ads) loaded after
loadevent - A real CDN serving static assets from a geographically close edge
INP killers we always find
- Heavy event listeners running on
scrollwithout throttling - A "newsletter popup" plugin attaching to every input
- Live chat scripts (Tawk.to, Intercom) doing work on first input
- Cookie consent banners with synchronous third-party calls
Step 7 β Decide caching last
Caching is the last step, not the first. Once you've made the underlying site fast, caching makes it instantaneous. But caching on a structurally slow site only hides the problem until the cache misses.
The caching stack we deploy
- Object cache (Redis or Memcached) β eliminates repeated database queries
- Page cache (Nginx microcache or WP Rocket) β serves HTML in microseconds
- CDN edge cache β Cloudflare or Bunny.net for static assets
- Browser cache headers β
Cache-Control: public, max-age=31536000, immutablefor versioned assets
The order matters: object cache first (reduces DB load), then page cache (reduces PHP load), then CDN (reduces origin requests).
Common diagnostic mistakes
- Trusting lab results over field data β Lighthouse on a fast laptop says everything is fine; real users on 4G say otherwise
- Optimizing the home page only β the home is the fastest page on most WordPress sites; bottlenecks live on category pages, search, single posts with comments
- Caching admin pages β never; admin must be uncached for accuracy
- Skipping the rebuild step β after changes, retest from scratch, don't assume
When to call a specialist
If you've spent more than 4 hours on a performance audit and the numbers haven't moved, the issue is structural β code-level, query-level, or infrastructure-level. We do this every week and find the root cause in 90 minutes on average.
WordPress speed recovery is the right service for sites stuck in the 3β10 second TTFB range. For sites with intermittent slowdowns, emergency support is faster.

