WP Performance (backend-only)
When to use
Use this skill when:
- a WordPress site/page/endpoint is slow (frontend TTFB, admin, REST, WP-Cron)
- you need a profiling plan and tooling recommendations (WP-CLI profile/doctor, Query Monitor, Xdebug/XHProf, APMs)
- you’re optimizing DB queries, autoloaded options, object caching, cron tasks, or remote HTTP calls
This skill assumes the agent cannot use a browser UI. Prefer WP-CLI, logs, and HTTP requests.
Inputs required
- Environment and safety: dev/staging/prod, any restrictions (no writes, no plugin installs).
- How to target the install:
- WP root
--path=<path>
- (multisite/site targeting)
--url=<url>
- The performance symptom and scope:
- which URL/REST route/admin screen
- when it happens (always vs sporadic; logged-in vs logged-out)
Procedure
0) Guardrails: measure first, avoid risky ops
- Confirm whether you may run write operations (plugin installs, config changes, cache flush).
- Pick a reproducible target (URL or REST route) and capture a baseline:
- TTFB/time with
curl if possible
- WP-CLI profiling if available
Read:
references/measurement.md
1) Generate a backend-only performance report (deterministic)
Run:
node skills/wp-performance/scripts/perf_inspect.mjs --path=<path> [--url=<url>]
This detects:
- WP-CLI availability and core version
- whether
wp doctor / wp profile are available
- autoloaded options size (if possible)
- object-cache drop-in presence
2) Fast wins: run diagnostics before deep profiling
If you have WP-CLI access, prefer:
It catches common production foot-guns (autoload bloat, SAVEQUERIES/WP_DEBUG, plugin counts, updates).
Read:
references/wp-cli-doctor.md
3) Deep profiling (no browser required)
Preferred order:
wp profile stage to see where time goes (bootstrap/main_query/template).
wp profile hook (optionally with --url=) to find slow hooks/callbacks.
wp profile eval for targeted code paths.
Read:
references/wp-cli-profile.md
4) Query Monitor (backend-only usage)
Query Monitor is normally UI-driven, but it can be used headlessly via REST API response headers and _envelope responses:
- Authenticate (nonce or Application Password).
- Request REST responses and inspect headers (
x-qm-*) and/or the qm property when using ?_envelope.
Read:
references/query-monitor-headless.md
5) Fix by category (choose the dominant bottleneck)
Use the profile output to pick one primary bottleneck category:
- DB queries → reduce query count, fix N+1 patterns, improve indexes, avoid expensive meta queries.
- Autoloaded options → identify the biggest autoloaded options and stop autoloading large blobs.
references/autoload-options.md
- Object cache misses → introduce caching or fix cache key/group usage; add persistent object cache where appropriate.
references/object-cache.md
- Remote HTTP calls → add timeouts, caching, batching; avoid calling remote APIs on every request.
- Cron → reduce due-now spikes, de-duplicate events, move heavy tasks out of request paths.
6) Verify (repeat the same measurement)
- Re-run the same
wp profile / wp doctor / REST request.
- Confirm the performance delta and that behavior is unchanged.
- If the fix is risky, ship behind a feature flag or staged rollout when possible.
WordPress 6.9 performance improvements
Be aware of these 6.9 changes when profiling:
On-demand CSS for classic themes:
- Classic themes now get on-demand CSS loading (previously only block themes had this).
- Reduces CSS payload by 30-65% by only loading styles for blocks actually used on the page.
- If you're profiling a classic theme, this should already be helping.
Block themes with no render-blocking resources:
- Block themes that don't define custom stylesheets (like Twenty Twenty-Three/Four) can now load with zero render-blocking CSS.
- Styles come from global styles (theme.json) and separate block styles, all inlined.
- This significantly improves LCP (Largest Contentful Paint).
Inline CSS limit increased:
- The threshold for inlining small stylesheets has been raised, reducing render-blocking resources.
Reference: https://make.wordpress.org/core/2025/11/18/wordpress-6-9-frontend-performance-field-guide/
Verification
- Baseline vs after numbers are captured (same environment, same URL/route).
wp doctor check is clean (or improved) when applicable.
- No new PHP errors or warnings in logs.
- No cache flush is required for correctness (cache flush should be last resort).
Failure modes / debugging
- “No change” after code changes:
- you measured a different URL/site (
--url mismatch), caches masked results, or opcode cache is stale
- Profiling data is noisy:
- eliminate background tasks, test with warmed caches, run multiple samples
SAVEQUERIES/Query Monitor causes overhead:
- don’t run in production unless explicitly approved
Escalation
- If this is production and you don’t have explicit approval, do not:
- install plugins, enable
SAVEQUERIES, run load tests, or flush caches during traffic
- If you need system-level profiling (APM, PHP profiler extensions), coordinate with ops/hosting.
1---2name: wp-performance3description: Use when investigating or improving WordPress performance (backend-only agent): profiling and measurement (WP-CLI profile/doctor, Server-Timing, Query Monitor via REST headers), database/query optimization, autoloaded options, object caching, cron, HTTP API calls, and safe verification.4---5
6# WP Performance (backend-only)
7
8## When to use
9
10Use this skill when:
11
12- a WordPress site/page/endpoint is slow (frontend TTFB, admin, REST, WP-Cron)
13- you need a profiling plan and tooling recommendations (WP-CLI profile/doctor, Query Monitor, Xdebug/XHProf, APMs)
14- you’re optimizing DB queries, autoloaded options, object caching, cron tasks, or remote HTTP calls
15
16This skill assumes the agent cannot use a browser UI. Prefer WP-CLI, logs, and HTTP requests.
17
18## Inputs required
19
20- Environment and safety: dev/staging/prod, any restrictions (no writes, no plugin installs).
21- How to target the install:
22 - WP root `--path=<path>`
23 - (multisite/site targeting) `--url=<url>`
24- The performance symptom and scope:
25 - which URL/REST route/admin screen
26 - when it happens (always vs sporadic; logged-in vs logged-out)
27
28## Procedure
29
30### 0) Guardrails: measure first, avoid risky ops
31
321. Confirm whether you may run write operations (plugin installs, config changes, cache flush).
332. Pick a reproducible target (URL or REST route) and capture a baseline:
34 - TTFB/time with `curl` if possible
35 - WP-CLI profiling if available
36
37Read:
38- `references/measurement.md`
39
40### 1) Generate a backend-only performance report (deterministic)
41
42Run:
43
44- `node skills/wp-performance/scripts/perf_inspect.mjs --path=<path> [--url=<url>]`
45
46This detects:
47
48- WP-CLI availability and core version
49- whether `wp doctor` / `wp profile` are available
50- autoloaded options size (if possible)
51- object-cache drop-in presence
52
53### 2) Fast wins: run diagnostics before deep profiling
54
55If you have WP-CLI access, prefer:
56
57- `wp doctor check`
58
59It catches common production foot-guns (autoload bloat, SAVEQUERIES/WP_DEBUG, plugin counts, updates).
60
61Read:
62- `references/wp-cli-doctor.md`
63
64### 3) Deep profiling (no browser required)
65
66Preferred order:
67
681. `wp profile stage` to see where time goes (bootstrap/main_query/template).
692. `wp profile hook` (optionally with `--url=`) to find slow hooks/callbacks.
703. `wp profile eval` for targeted code paths.
71
72Read:
73- `references/wp-cli-profile.md`
74
75### 4) Query Monitor (backend-only usage)
76
77Query Monitor is normally UI-driven, but it can be used headlessly via REST API response headers and `_envelope` responses:
78
79- Authenticate (nonce or Application Password).
80- Request REST responses and inspect headers (`x-qm-*`) and/or the `qm` property when using `?_envelope`.
81
82Read:
83- `references/query-monitor-headless.md`
84
85### 5) Fix by category (choose the dominant bottleneck)
86
87Use the profile output to pick *one* primary bottleneck category:
88
89- **DB queries** → reduce query count, fix N+1 patterns, improve indexes, avoid expensive meta queries.
90 - `references/database.md`
91- **Autoloaded options** → identify the biggest autoloaded options and stop autoloading large blobs.
92 - `references/autoload-options.md`
93- **Object cache misses** → introduce caching or fix cache key/group usage; add persistent object cache where appropriate.
94 - `references/object-cache.md`
95- **Remote HTTP calls** → add timeouts, caching, batching; avoid calling remote APIs on every request.
96 - `references/http-api.md`
97- **Cron** → reduce due-now spikes, de-duplicate events, move heavy tasks out of request paths.
98 - `references/cron.md`
99
100### 6) Verify (repeat the same measurement)
101
102- Re-run the same `wp profile` / `wp doctor` / REST request.
103- Confirm the performance delta and that behavior is unchanged.
104- If the fix is risky, ship behind a feature flag or staged rollout when possible.
105
106## WordPress 6.9 performance improvements
107
108Be aware of these 6.9 changes when profiling:
109
110**On-demand CSS for classic themes:**
111- Classic themes now get on-demand CSS loading (previously only block themes had this).
112- Reduces CSS payload by 30-65% by only loading styles for blocks actually used on the page.
113- If you're profiling a classic theme, this should already be helping.
114
115**Block themes with no render-blocking resources:**
116- Block themes that don't define custom stylesheets (like Twenty Twenty-Three/Four) can now load with zero render-blocking CSS.
117- Styles come from global styles (theme.json) and separate block styles, all inlined.
118- This significantly improves LCP (Largest Contentful Paint).
119
120**Inline CSS limit increased:**
121- The threshold for inlining small stylesheets has been raised, reducing render-blocking resources.
122
123Reference: https://make.wordpress.org/core/2025/11/18/wordpress-6-9-frontend-performance-field-guide/
124
125## Verification
126
127- Baseline vs after numbers are captured (same environment, same URL/route).
128- `wp doctor check` is clean (or improved) when applicable.
129- No new PHP errors or warnings in logs.
130- No cache flush is required for correctness (cache flush should be last resort).
131
132## Failure modes / debugging
133
134- “No change” after code changes:
135 - you measured a different URL/site (`--url` mismatch), caches masked results, or opcode cache is stale
136- Profiling data is noisy:
137 - eliminate background tasks, test with warmed caches, run multiple samples
138- `SAVEQUERIES`/Query Monitor causes overhead:
139 - don’t run in production unless explicitly approved
140
141## Escalation
142
143- If this is production and you don’t have explicit approval, do not:
144 - install plugins, enable `SAVEQUERIES`, run load tests, or flush caches during traffic
145- If you need system-level profiling (APM, PHP profiler extensions), coordinate with ops/hosting.