From 9903dc17244c9c78fcf374d951bf06b2be07837e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:46:58 +0000 Subject: [PATCH 1/6] Initial plan From e4de1d2a5da7db774e43f6a8a02d4a1ae5352981 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:51:06 +0000 Subject: [PATCH 2/6] Add how-to guide: Figure out why WordPress is slow Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- bin/handbook-manifest.json | 6 ++ how-to.md | 2 + how-to/figure-out-why-wordpress-is-slow.md | 83 ++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 how-to/figure-out-why-wordpress-is-slow.md diff --git a/bin/handbook-manifest.json b/bin/handbook-manifest.json index c614118c0..81c9330e1 100644 --- a/bin/handbook-manifest.json +++ b/bin/handbook-manifest.json @@ -341,6 +341,12 @@ "markdown_source": "https:\/\/github.com\/wp-cli\/handbook\/blob\/main\/how-to\/how-to-start-webserver.md", "parent": "how-to" }, + "figure-out-why-wordpress-is-slow": { + "title": "Figure out why WordPress is slow", + "slug": "figure-out-why-wordpress-is-slow", + "markdown_source": "https:\/\/github.com\/wp-cli\/handbook\/blob\/main\/how-to\/figure-out-why-wordpress-is-slow.md", + "parent": "how-to" + }, "identify-plugin-theme-conflict": { "title": "Identify a Plugin or Theme Conflict", "slug": "identify-plugin-theme-conflict", diff --git a/how-to.md b/how-to.md index 2dab54760..4b7b0146f 100644 --- a/how-to.md +++ b/how-to.md @@ -12,3 +12,5 @@ tasks. - [How-to start the webserver](https://make.wordpress.org/cli/handbook/) - [How to create a custom plugin](https://make.wordpress.org/cli/handbook/how-to-create-custom-plugins) + +- [Figure out why WordPress is slow](https://make.wordpress.org/cli/handbook/figure-out-why-wordpress-is-slow) diff --git a/how-to/figure-out-why-wordpress-is-slow.md b/how-to/figure-out-why-wordpress-is-slow.md new file mode 100644 index 000000000..c80d1eb01 --- /dev/null +++ b/how-to/figure-out-why-wordpress-is-slow.md @@ -0,0 +1,83 @@ +# Figure out why WordPress is slow + +With `wp profile`, you gain quick visibility into key performance metrics (execution time, query count, cache hit/miss ratio, etc.) to guide further debugging. + +`wp profile` is a WP-CLI command to help you quickly identify what's slow with WordPress. It's designed to work alongside Xdebug and New Relic because it's easy to deploy to any server that has WP-CLI. With `wp profile`, you gain quick visibility into key performance metrics (execution time, query count, cache hit/miss ratio, etc.) to guide further debugging. + +### Step 1 - Profile the WordPress load process + +Dealing with a slow WordPress install you've never worked with before? Run `wp profile stage` to see metrics for each stage of the WordPress load process. Include the `--url=` argument to mock the request as a specific URL. + +``` +$ wp profile stage --url=runcommand.io ++------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+ +| stage | time | query_time | query_count | cache_ratio | cache_hits | cache_misses | hook_time | hook_count | request_time | request_count | ++------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+ +| bootstrap | 0.7597s | 0.0052s | 14 | 93.21% | 357 | 26 | 0.3328s | 2717 | 0s | 0 | +| main_query | 0.0131s | 0.0004s | 3 | 94.29% | 33 | 2 | 0.0065s | 78 | 0s | 0 | +| template | 0.7041s | 0.0192s | 147 | 92.16% | 2350 | 200 | 0.6982s | 6130 | 0s | 0 | ++------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+ +| total (3) | 1.477s | 0.0248s | 164 | 93.22% | 2740 | 228 | 1.0375s | 8925 | 0s | 0 | ++------------+---------+------------+-------------+-------------+------------+--------------+-----------+------------+--------------+---------------+ +``` + +When WordPress handles a request from a browser, it's essentially executing as one long PHP script. `wp profile stage` breaks the script into three stages: + +* **bootstrap** is where WordPress is setting itself up, loading plugins and the main theme, and firing the init hook. +* **main_query** is how WordPress transforms the request (e.g. /2016/10/21/moms-birthday/) into the primary WP_Query. +* **template** is where WordPress determines which theme template to render based on the main query, and renders it. + +### Step 2 - Dive into a specific stage + +In the example from above, bootstrap seems a bit slow, so let's dive into it further. Run `wp profile stage bootstrap` to dive into higher fidelity mode of a given stage. Use the `--spotlight` flag to filter out the zero-ish results. + +``` +$ wp profile stage bootstrap --url=runcommand.io --spotlight ++--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+ +| hook | callback_count | time | query_time | query_count | cache_ratio | cache_hits | cache_misses | request_time | request_count | ++--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+ +| muplugins_loaded:before | | 0.1644s | 0.0017s | 1 | 40% | 2 | 3 | 0s | 0 | +| muplugins_loaded | 2 | 0.0005s | 0s | 0 | 50% | 1 | 1 | 0s | 0 | +| plugins_loaded:before | | 0.1771s | 0.0008s | 6 | 77.63% | 59 | 17 | 0s | 0 | +| plugins_loaded | 14 | 0.0887s | 0s | 0 | 100% | 104 | 0 | 0s | 0 | +| after_setup_theme:before | | 0.043s | 0s | 0 | 100% | 26 | 0 | 0s | 0 | +| init | 82 | 0.1569s | 0.0018s | 7 | 96.88% | 155 | 5 | 0s | 0 | +| wp_loaded:after | | 0.027s | 0s | 0 | | 0 | 0 | 0s | 0 | ++--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+ +| total (7) | 98 | 0.6575s | 0.0043s | 14 | 77.42% | 347 | 26 | 0s | 0 | ++--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+ +``` + +Each stage is further segmented by `wp profile` based on its primary actions. For the bootstrap stage, the primary actions include 'muplugins_loaded', 'plugins_loaded', and 'init'. You can also see some intermediate actions like 'plugins_loaded:before' and 'wp_loaded:after'. These intermediate actions correspond to script execution before (or after) actual WordPress hooks. They're pseudo hooks in a sense. + +### Step 3 - Profile specific hooks + +When you've found a specific hook you'd like to assess, run `wp profile hook `. Include the `--fields=` argument to limit output to certain fields. + +``` +$ wp profile hook plugins_loaded --url=runcommand.io --fields=callback,time,location ++------------------------------------------------------------+---------+-----------------------------------------------------------------+ +| callback | time | location | ++------------------------------------------------------------+---------+-----------------------------------------------------------------+ +| wp_maybe_load_widgets() | 0.0046s | wp-includes/functions.php:3501 | +| wp_maybe_load_embeds() | 0.0003s | wp-includes/embed.php:162 | +| VaultPress_Hotfixes->protect_jetpack_402_from_oembed_xss() | 0s | vaultpress/class.vaultpress-hotfixes.php:124 | +| _wp_customize_include() | 0s | wp-includes/theme.php:2052 | +| EasyRecipePlus->pluginsLoaded() | 0.0013s | easyrecipeplus/lib/EasyRecipePlus.php:125 | +| Gamajo\GenesisHeaderNav\genesis_header_nav_i18n() | 0.0007s | genesis-header-nav/genesis-header-nav.php:61 | +| DS_Public_Post_Preview::init() | 0.0001s | public-post-preview/public-post-preview.php:52 | +| wpseo_load_textdomain() | 0.0004s | wordpress-seo-premium/wp-seo-main.php:222 | +| load_yoast_notifications() | 0.0016s | wordpress-seo-premium/wp-seo-main.php:381 | +| wpseo_init() | 0.0329s | wordpress-seo-premium/wp-seo-main.php:240 | +| wpseo_premium_init() | 0.0019s | wordpress-seo-premium/wp-seo-premium.php:79 | +| wpseo_frontend_init() | 0.0007s | wordpress-seo-premium/wp-seo-main.php:274 | +| Black_Studio_TinyMCE_Plugin->load_compatibility() | 0.0016s | black-studio-tinymce-widget/black-studio-tinymce-widget.php:206 | +| Jetpack::load_modules() | 0.0564s | jetpack/class.jetpack.php:1672 | ++------------------------------------------------------------+---------+-----------------------------------------------------------------+ +| total (14) | 0.1026s | | ++------------------------------------------------------------+---------+-----------------------------------------------------------------+ +``` + +Et voila! We've discovered that `wpseo_init()` and `Jetpack::load_modules()` are collectively contributing ~100ms to every page load. + +With `wp profile`, discovering why WordPress is slow becomes the easy part. From c05b7b3d00a2171156e2a4891a3896b49da2e4b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 12:52:13 +0000 Subject: [PATCH 3/6] Address code review feedback: remove duplication and improve language Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- how-to/figure-out-why-wordpress-is-slow.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/how-to/figure-out-why-wordpress-is-slow.md b/how-to/figure-out-why-wordpress-is-slow.md index c80d1eb01..c10b37986 100644 --- a/how-to/figure-out-why-wordpress-is-slow.md +++ b/how-to/figure-out-why-wordpress-is-slow.md @@ -1,7 +1,5 @@ # Figure out why WordPress is slow -With `wp profile`, you gain quick visibility into key performance metrics (execution time, query count, cache hit/miss ratio, etc.) to guide further debugging. - `wp profile` is a WP-CLI command to help you quickly identify what's slow with WordPress. It's designed to work alongside Xdebug and New Relic because it's easy to deploy to any server that has WP-CLI. With `wp profile`, you gain quick visibility into key performance metrics (execution time, query count, cache hit/miss ratio, etc.) to guide further debugging. ### Step 1 - Profile the WordPress load process @@ -78,6 +76,6 @@ $ wp profile hook plugins_loaded --url=runcommand.io --fields=callback,time,loca +------------------------------------------------------------+---------+-----------------------------------------------------------------+ ``` -Et voila! We've discovered that `wpseo_init()` and `Jetpack::load_modules()` are collectively contributing ~100ms to every page load. +There you have it! We've discovered that `wpseo_init()` and `Jetpack::load_modules()` are collectively contributing ~100ms to every page load. With `wp profile`, discovering why WordPress is slow becomes the easy part. From 8d2c899c66b69c157a57be2d1fa5231517bbc2b5 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 12 Jan 2026 14:21:23 +0100 Subject: [PATCH 4/6] Update how-to/figure-out-why-wordpress-is-slow.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- how-to/figure-out-why-wordpress-is-slow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/figure-out-why-wordpress-is-slow.md b/how-to/figure-out-why-wordpress-is-slow.md index c10b37986..2786f19ad 100644 --- a/how-to/figure-out-why-wordpress-is-slow.md +++ b/how-to/figure-out-why-wordpress-is-slow.md @@ -46,7 +46,7 @@ $ wp profile stage bootstrap --url=runcommand.io --spotlight +--------------------------+----------------+---------+------------+-------------+-------------+------------+--------------+--------------+---------------+ ``` -Each stage is further segmented by `wp profile` based on its primary actions. For the bootstrap stage, the primary actions include 'muplugins_loaded', 'plugins_loaded', and 'init'. You can also see some intermediate actions like 'plugins_loaded:before' and 'wp_loaded:after'. These intermediate actions correspond to script execution before (or after) actual WordPress hooks. They're pseudo hooks in a sense. +Each stage is further segmented by `wp profile` based on its primary actions. For the bootstrap stage, the primary actions include `muplugins_loaded`, `plugins_loaded`, and `init`. You can also see some intermediate actions like `plugins_loaded:before` and `wp_loaded:after`. These intermediate actions correspond to script execution before (or after) actual WordPress hooks. They're pseudo hooks in a sense. ### Step 3 - Profile specific hooks From f84b0e09d0d24670f597449a5087cfa0d46580b8 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 12 Jan 2026 14:21:29 +0100 Subject: [PATCH 5/6] Update how-to/figure-out-why-wordpress-is-slow.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- how-to/figure-out-why-wordpress-is-slow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/how-to/figure-out-why-wordpress-is-slow.md b/how-to/figure-out-why-wordpress-is-slow.md index 2786f19ad..2528e4861 100644 --- a/how-to/figure-out-why-wordpress-is-slow.md +++ b/how-to/figure-out-why-wordpress-is-slow.md @@ -21,7 +21,7 @@ $ wp profile stage --url=runcommand.io When WordPress handles a request from a browser, it's essentially executing as one long PHP script. `wp profile stage` breaks the script into three stages: -* **bootstrap** is where WordPress is setting itself up, loading plugins and the main theme, and firing the init hook. +* **bootstrap** is where WordPress is setting itself up, loading plugins and the main theme, and firing the `init` hook. * **main_query** is how WordPress transforms the request (e.g. /2016/10/21/moms-birthday/) into the primary WP_Query. * **template** is where WordPress determines which theme template to render based on the main query, and renders it. From 49f36f5fa6792f0370dd60b4ae01a948574c445d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:23:30 +0000 Subject: [PATCH 6/6] Add installation instructions for wp profile package Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- how-to/figure-out-why-wordpress-is-slow.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/how-to/figure-out-why-wordpress-is-slow.md b/how-to/figure-out-why-wordpress-is-slow.md index 2528e4861..3f6b114bc 100644 --- a/how-to/figure-out-why-wordpress-is-slow.md +++ b/how-to/figure-out-why-wordpress-is-slow.md @@ -2,6 +2,14 @@ `wp profile` is a WP-CLI command to help you quickly identify what's slow with WordPress. It's designed to work alongside Xdebug and New Relic because it's easy to deploy to any server that has WP-CLI. With `wp profile`, you gain quick visibility into key performance metrics (execution time, query count, cache hit/miss ratio, etc.) to guide further debugging. +## Installation + +First, install the `wp profile` package if you haven't already: + +``` +$ wp package install wp-cli/profile-command +``` + ### Step 1 - Profile the WordPress load process Dealing with a slow WordPress install you've never worked with before? Run `wp profile stage` to see metrics for each stage of the WordPress load process. Include the `--url=` argument to mock the request as a specific URL.