Why the WooCommerce dashboard is slow, and how to find the cause
The WooCommerce dashboard is built by the server on every click, with no page cache to help. Measure one slow screen with Query Monitor, then check order storage, the Scheduled Actions queue, the options table, Analytics, the object cache, plugins and the server, in that order.
- By
- WP Ministry
- Published
In short
- A page cache does nothing for the dashboard. What helps is less work on each request and a server that does it faster.
- Measure before you change anything. Read the server's wait in the browser's Network panel, then use Query Monitor on the slow screen.
- If only Orders is slow, look at "Order data storage" under WooCommerce, Settings, Advanced, Features.
- Read the counts on the Scheduled Actions screen. Several past-due actions more than a day old suggest background jobs are stuck.
- If nobody reads the Analytics reports, switch Analytics off under Features. If somebody does, set its "Updates" to "Scheduled".
- Export the database before anything that deletes, and keep the file outside the folder the site is served from.
A slow WooCommerce dashboard is a slow answer from the server. WordPress sends every dashboard page with headers that prevent caching, so each click on Orders, a product or a report waits while PHP runs WooCommerce and every active plugin, and the database answers their queries. The shop itself can stay quick the whole time, because a page cache hands visitors stored pages.
So the job is to find which work takes the time, and reduce it. Measure one slow screen first, then check the causes below in order. The first four are particular to a store. The rest apply to any WordPress site, or belong to your host.
Measure before you change anything
Step 1: Read the server's wait in the browser
Open the slow screen with the browser's developer tools on the Network panel. Select the first request in the list, which is the page itself, and open its Timing tab. Chrome's documentation calls the phase to read "Waiting (TTFB)": the wait for the first byte of the answer, which includes the time the server took to prepare it. If that phase is most of the wait, the server is the slow part and this page applies.
Step 2: See whether every screen is slow, or only WooCommerce's
Time three screens the same way: Posts, then Orders, then the one you find slowest. If Posts is quick and Orders is not, look at WooCommerce's own data, starting with order storage. If every screen is slow, the cause sits under all of them: the object cache, the options table, a plugin that runs everywhere, or the server.
Step 3: Install Query Monitor and note its four figures
Query Monitor is a plugin in the WordPress.org directory. Its listing says its output is shown only to Administrators by default, that it does not persistently store what it collects, and that it sends nothing to a third party. It adds a menu to the admin toolbar with four figures for the screen you are on: page generation time in seconds, peak memory usage, total time taken by SQL queries, and the number of SQL queries. Write them down. If the query time is most of the generation time, look at the database. If it is a small part, look at outside requests and at PHP itself.
Step 4: Read three panels on the slow screen
- Timeline plots the queries, outside requests and other events of the page load along a line. Query Monitor's documentation calls it the quickest way to see where the time went.
- Queries, then Queries by Component, groups the database queries by the plugin or theme responsible, sorted by total time. Slow Queries and Duplicate Queries list the ones worth a closer look.
- HTTP API Calls lists requests your server made to other servers while it built the page, each with its response code, the component responsible and the time taken.
Some screens draw first and fetch their figures afterwards. Filter the Network panel to "Fetch/XHR" to see those requests and how long each took. To time a request in phases from the command line, see how to reduce server response time.
The wp commands below need SSH access and WP-CLI, and are run from the site's main folder. Where one names a table, wp_ is the default prefix: use your own.
Order storage: posts or High-Performance Order Storage
WooCommerce can keep orders in two places. The older one is the posts and postmeta tables, which WordPress also uses for pages and posts. High-Performance Order Storage, or HPOS, keeps orders in four tables of their own, with their own indexes. WooCommerce's documentation gives the result as fewer read and write operations and fewer busy tables. HPOS is the default for stores first installed with WooCommerce 8.2 or later, so an older store may still be on posts.
How to check. Go to WooCommerce, then Settings, then Advanced, then Features, and find "Order data storage". The two choices are "WordPress posts storage (legacy)" and "High-performance order storage (recommended)". Under them is a checkbox whose label begins "Enable compatibility mode". With WP-CLI, one command prints the same facts:
wp wc hpos statusIt answers with lines such as HPOS enabled?: yes and Compatibility mode enabled?: no, and a count of orders not yet synchronized.
What to do with the answer.
- Posts storage is selected. Orders share their tables with every page and post. If Orders is your slow screen, this is the first thing to change. The switch needs a backup and a rehearsal on a copy. The steps are in how to speed up a WooCommerce store.
- HPOS is selected and compatibility mode is on. WooCommerce keeps both sets of tables in step. Its developer documentation says each change to an order is applied to the second set as soon as it is made, so every order is written twice. WooCommerce advises keeping compatibility mode on for some time after a switch. Once every extension is known to work with HPOS, turn it off.
- HPOS is selected and compatibility mode is off. Order storage is not your problem.
Scheduled actions: a backlog, or a log that has grown
Action Scheduler is the queue WooCommerce and its extensions use for background jobs. Each job is a row in its own tables, whose names begin actionscheduler_, and each event for each job is logged in actionscheduler_logs. Two things go wrong with it.
- A backlog. The queue is started by WP-Cron and also by the dashboard: once a minute, if actions are pending, a dashboard request sets off a background request to run them. A queue that never empties takes a share of the server while you work.
- An oversized log. Action Scheduler deletes actions that were completed or canceled more than a month ago. Since version 4.0.0 it also deletes failed ones after three months. Before that version it kept failed actions indefinitely.
How to check. Go to WooCommerce, then Status, then Scheduled Actions. Above the table is a row of links, one for each status in use, with a count beside each: "Complete", "Pending", "Failed", "Past-due". The System status tab has an "Action Scheduler" section with the same counts and the oldest and newest date for each status. With WP-CLI:
wp action-scheduler statusFixing a backlog. Action Scheduler's documentation says some past-due actions are normal, because of how WP-Cron works, and that several of them more than a day old may mean something is wrong with the site. Select "Failed" and open an action's log entries to see why it failed. If scheduled posts are late too, the trouble is WP-Cron itself: see how to fix a missed schedule.
Do not delete pending actions to clear a backlog. Each is a job that has not run, and the documentation lists emails and payments among them. For a large queue it recommends WP-CLI over the default runner: wp action-scheduler run. That runs what is due, so whatever those jobs do, such as sending email, happens then.
Cleaning the log. Do this when the "Complete" or "Failed" count is very large, or the oldest date is older than the periods above.
Step 1: Export the database first
A deleted action comes back only from a backup. Write the export to your home folder, never to the folder the site is served from, where anyone who guessed the file's name could download it.
bashwp db export ~/before-cleanup.sqlStep 2: Delete old completed and canceled actions
This deletes actions with the status complete or canceled whose scheduled date is more than 31 days ago, 20 per status at a time, until none are left. The log entries of each deleted action go with it. Pending, in-progress and failed actions are not touched.
bashwp action-scheduler cleanStep 3: Delete old failed actions, if you no longer need them
This example is from Action Scheduler's documentation. It deletes failed actions scheduled more than 90 days ago, 50 at a time, with a two-second pause between batches. The record of why they failed goes with them.
bashwp action-scheduler clean --status=failed --before='90 days ago' --batch-size=50 --pause=2
To keep less from then on, change the retention period. It is a filter, action_scheduler_retention_period, which takes a number of seconds. This keeps completed and canceled actions for a week:
<?php
add_filter( 'action_scheduler_retention_period', function () {
return 7 * DAY_IN_SECONDS;
} );Create the mu-plugins folder if it is not there. A shorter period leaves less history to read when a job goes wrong.
The options table: autoloaded options, transients and sessions
Autoloaded options. These are settings WordPress loads with every page load, dashboard screens included. WordPress's performance guide says too many can slow a site and advises keeping the total under 800 KB. Tools, then Site Health reports the count and the size. This measures the same thing:
wp db query "SELECT COUNT(*) AS options, SUM(LENGTH(option_value)) AS bytes FROM wp_options WHERE autoload IN ('yes','on','auto','auto-on');"The four values are the ones WordPress treats as "load this every time". WordPress's guide gives no steps for trimming and refers you to your host. How to clean up and optimize a WordPress database has them.
Transients. A transient is a value stored for a set time. Without a persistent object cache, WordPress keeps transients as rows in the options table. Two tools under WooCommerce, then Status, then Tools clear them. "Expired transients" removes only what has expired, as does wp transient delete --expired. "WooCommerce transients" empties the shop and product cache, which WooCommerce then has to fill again. It is for product data that looks out of date, not for speed.
Sessions. The woocommerce_sessions table holds each visitor's session, cart contents included. WooCommerce deletes sessions past their expiry itself, with a scheduled action named woocommerce_cleanup_sessions that recurs every 12 hours. So a table full of expired rows is a second sign that scheduled actions are stuck. The System status tab lists every table with its size. This counts the rows, and how many have expired:
wp db query "SELECT COUNT(*) AS sessions, SUM(session_expiry < UNIX_TIMESTAMP()) AS expired FROM wp_woocommerce_sessions;"Fix the queue first, and WooCommerce clears the expired rows itself. To remove them now, export the database as above, then run the statement below at a quiet hour. It deletes the sessions whose expiry time has passed, which is the condition WooCommerce's own cleanup uses. Sessions that have not expired, and the carts in them, stay.
wp db query "DELETE FROM wp_woocommerce_sessions WHERE session_expiry < UNIX_TIMESTAMP();"The "Clear customer sessions" tool is not the same thing. Its description says it "will delete all customer session data from the database, including current carts and saved carts in the database."
WooCommerce Analytics
Analytics is the set of reports under the Analytics menu: revenue, orders, products, customers and more. WooCommerce's documentation says it has to process a large amount of data to produce them, and that it keeps the results in a usable form and updates them regularly. That form is a set of tables of its own, such as wc_order_stats and wc_order_product_lookup, filled from your orders by scheduled actions.
How to check. While a report loads, filter the Network panel to "Fetch/XHR" and look for slow requests with wc-analytics in the address. On the Scheduled Actions screen, search for wc-admin to see whether its jobs are piling up.
Settings that reduce the load. They are under Analytics, then Settings.
- "Updates", from WooCommerce 10.5. "Scheduled" updates the Analytics data every 12 hours. The documentation says it has the lowest performance impact and recommends it for most stores. "Immediately" updates as soon as new data is available, which the documentation says may affect performance on busy stores.
- "Import Historical Data". The documentation suggests that very large stores import in smaller pieces, a year or a quarter at a time, with "Skip previously imported customers and orders" selected.
Switching it off. If nobody reads these reports, go to WooCommerce, then Settings, then Advanced, then Features, and clear the "WooCommerce Analytics" checkbox. In WooCommerce 11.2 the Analytics menu goes, and so does the Customers screen under WooCommerce, which is one of its reports. Orders, products and customers themselves are not touched. Select it again to bring the reports back.
A persistent object cache
WordPress keeps what it has read from the database in an object cache. By default that cache lasts for one request and is then thrown away. A persistent object cache keeps it between requests. WordPress's performance guide gives the example of the site's options: without a persistent cache, the server reads them from the database on every page view. With one, transients leave the options table too.
This cache does help the dashboard, because it works below the level of the page. It needs a cache server, such as Redis or Memcached, which your host has to offer.
How to check. Under Tools, then Site Health, WordPress shows "You should use a persistent object cache" when it judges the site would benefit. WooCommerce, then Status has an "External object cache" row. Query Monitor's Overview panel shows "External object cache not in use" when there is none.
When it helps. It helps when Query Monitor shows the same queries on every screen. It does not make one slow query fast the first time it runs. On a store with HPOS, the Features screen has "HPOS Data Caching", which WooCommerce recommends for stores using object caching.
Heartbeat and admin-ajax.php
WordPress's Heartbeat API is a polling system: an open dashboard page sends a request to the server every 15 to 120 seconds, and admin-ajax.php answers it. A store with several people at work, each with a few order and product tabs open, sends a steady stream of them.
How to check. Leave a screen open with the Network panel filtered to "Fetch/XHR" and watch the admin-ajax.php requests arrive. If each takes seconds, the ticks are slow for the same reason your clicks are.
What to do. Close the dashboard tabs you are not using, then fix the slow server underneath. Slowing Heartbeat down treats the symptom.
Plugins that run on every screen, dashboard widgets and outside requests
- A plugin at work on screens that are not its own. Open a plain screen such as Posts and read Queries by Component. A plugin near the top of that list there is running everywhere.
- Requests to other servers. Query Monitor's documentation calls these the usually invisible causes of a slow site, and notes that they can occur sporadically. A check against an extension's own server for a license or an update is one kind. If a screen is slow only now and then, reload it several times with HTTP API Calls open and look for a call that is slow or has failed.
- Dashboard widgets. The first screen after login runs every widget on it. WooCommerce 11.2 adds "WooCommerce Status" and "WooCommerce Recent Reviews", and other plugins add theirs. Unchecking a widget under Screen Options hides it, but WordPress still calls the widget's function and only hides the box, so the work is still done. Use Query Monitor on the Dashboard screen to find a slow one, then switch it off in its plugin's settings or have a developer remove it.
To prove a plugin is the cause, deactivate it on a staging copy and measure the same screen again. How to fix WordPress plugin conflicts has the method.
PHP version and workers: what the host controls
The PHP version. WooCommerce recommends PHP 8.3 or greater, MySQL 8.0 or MariaDB 10.6 or greater, and a WordPress memory limit of at least 256 MB. WordPress's performance guide notes that newer PHP versions usually perform better. The System status tab shows what the store runs on, and the PHP end-of-life checker shows whether that version is still supported. The steps for changing it are in how to speed up WordPress.
Workers. On a server that runs PHP-FPM, each request in progress occupies one PHP process, and the setting pm.max_children caps how many there can be. Hosts often call them workers. One click in the dashboard is the page, the requests it makes afterwards, Heartbeat ticks from every open tab, and Action Scheduler's background run. When every process is busy, the next request waits in a queue. That shows as a dashboard that is slow in bursts, most of all when several people are working.
You cannot see or change this from WordPress. Ask your host how many PHP processes the plan allows, whether that limit has been reached, and whether they can switch on PHP's slow log, which records what PHP was running for any request that took longer than a set time.
A screen that stops with an error after a long wait has hit a limit: see maximum execution time exceeded and allowed memory size exhausted.
Products with many variations
Every variation of a variable product carries its own price, stock, image and more. WooCommerce's documentation treats the count as a cost. On the product page, a product with more than 30 variations gets simpler drop-down menus, and the documentation gives the reason: working out the available combinations after each selection can slow things down. In the editor, variations are shown a page at a time, with navigation arrows once there are more than 15.
How to check. If the slowness is on particular products, count their variations and run Query Monitor on the edit screen of one.
What to do.
- Edit in bulk. The "Bulk actions" menu at the top of the Variations tab applies a change to every variation of the product at once.
- Consider splitting a product with hundreds of combinations into several products.
- Under WooCommerce, then Status, then Tools, "Product lookup tables" rebuilds the tables WooCommerce keeps for quick access to product data, for when they are out of step with the products. "Orphaned variations" deletes all variations which have no parent product. Export the database before that one.
From symptom to likely cause
| What you see | Check first |
|---|---|
| Orders and order search are slow, other screens are fine | Order storage, and compatibility mode |
| Every screen is slow, Posts and Plugins included | Object cache, autoloaded options, a plugin that runs everywhere, the PHP version |
| Only the first screen after login is slow | Dashboard widgets and outside requests |
| Analytics and its reports are slow | Analytics settings, and its scheduled actions |
| Slow in bursts, or when several people are working | PHP workers, Heartbeat from open tabs, a scheduled actions backlog |
| Slow now and then, with no pattern | An outside request in HTTP API Calls |
| One product is slow to open or save | The number of variations |
| The Scheduled Actions screen itself is slow | The size of the scheduled actions log |
What not to do
- Do not delete rows from tables by hand without a backup. A pending action is a job that has not run. Use the tool or the documented command where there is one, and export the database first.
- Do not use "Clear customer sessions" as a speed fix. It empties every cart.
- Do not expect an "optimizer" plugin that promises everything to fix the dashboard. Page caching, minifying and image compression act on pages served to visitors. A plugin added to speed up the dashboard is also one more plugin that runs on every dashboard request. Measure the same screen before and after.
- Do not switch off Action Scheduler to quiet the server. The store's emails, payments and extension jobs go through it.
When to get help
Get help when the measurements point at the server and your host says it is not the server, when Query Monitor names code nobody on your side can read, or when the store is too busy to rehearse a change on. Our speed optimization service is a one-time job, with measurements before and after on the pages you name.
The weekly round in how to maintain a WooCommerce store includes reading the failed scheduled actions, which is where a backlog shows first.
Common questions
Why is the WooCommerce dashboard slow when the shop is fast?
On a store with a page cache, visitors are given stored pages, which take the server very little work. Dashboard screens are built by PHP and the database every time. The cache hides the server's real speed from visitors, and the dashboard shows it.
Will a caching plugin speed up the dashboard?
A page cache will not, because dashboard screens are not served from one. A persistent object cache can, because it saves the server from reading the same data from the database on every request. It needs a cache server such as Redis or Memcached from your host.
Is it safe to delete completed scheduled actions?
Completed and canceled actions are a record of jobs that have finished, and Action Scheduler deletes them itself after a month. Deleting old ones sooner with wp action-scheduler clean loses only that history. Export the database first, and leave pending actions alone.
Does switching off Analytics delete my orders?
No. Analytics keeps its figures in tables of its own, and your orders, products and customers stay where they are. Clearing the "WooCommerce Analytics" checkbox under Features removes the reports from the dashboard. Selecting it again brings them back.
- Error fixHow to fix WooCommerce payment gateway errors
- ResourceWooCommerce checkout is down: a runbook
- Cost guideWooCommerce maintenance cost: why a store costs more, and what the extra should buy
- ResourceWooCommerce sale readiness checklist: Black Friday or any big sale
- Error fixHow to fix a WooCommerce checkout that is not working
- GuideWooCommerce variations and SEO: variation URLs, canonical links and the price in structured data

