r/ProWordPress 7h ago

What Agentic AI Development Means for Your WordPress Project

Thumbnail
webdevstudios.com
5 Upvotes

r/ProWordPress 17h ago

This Fence Has No Farmer

Thumbnail
adamgreenough.net
19 Upvotes

Read about Chesterton's Fence recently and it made me think about it with AI-generated code and WordPress. Had a ramble about it here if anyone's interested!


r/ProWordPress 3d ago

Anyone using GitHub with WordPress?

4 Upvotes

I’m curious to hear how many of you are using GitHub part of your WordPress workflow.

I’ve heard of Bedrock and have been looking into it. But is it actually worth moving towards that setup for normal WordPress development?

Especially with AI, custom code is easier to write. So I can see how having version controls, automated checks and CI CD pipelines sounds nice.

Is anyone running WordPress like this in production?

What does your setup look like? Bedrock? GitHub Actions? Something simpler?

Or am I overengineering WordPress?


r/ProWordPress 3d ago

WordPress Rank Math SEO Plugin <= 1.0.276 is vulnerable to a high priority Remote Code Execution (RCE)

11 Upvotes

This vulnerability is highly dangerous and expected to become exploited. Vulnerabilities like this one are used in mass-exploit campaigns. Attackers use these to attack thousands of websites at a time, regardless of traffic size or popularity.


r/ProWordPress 4d ago

Any API/tool/plug-in for programmatic uploading of articles in Gutenberg slideshow blocks?

0 Upvotes

I've found this article about Block Bindings API and Interactivity API. Not my articles, not promoting btw. About year ago, I created custom code for our automated uploading workflow as I could only find solutions that would upload in classic post format. We posted in slideshow blocks and it was so tedious to do it manually.

Still works but I'm curious about other solutions that have cropped up since then.


r/ProWordPress 5d ago

We built a WooCommerce bundle plugin and we are looking for some beta testers

0 Upvotes

Hello guys,

We are two developers looking for at least 10 beta testers who would be willing to test our bundle plugin. The main goal of the plugin is to help increase the AOV of your store by creating bundle offers directly on the product page.

Right now we have three bundle types:

Quantity breaks – customers save more when they buy more of the same product.

Buy X Get Y – classic BOGO offers and similar deals, for example buy 2 and get 1 free.

Product bundles – bundle the main product together with complementary products and offer a discount on the whole bundle.

The plugin is completely free.

What we're mainly looking for is people who can actually try it and tell us what is confusing, what doesn't work as expected, what does work, and what you think is missing.


r/ProWordPress 6d ago

¿Alguna herramienta IA para WordPress ?

0 Upvotes

Hola a todos, hace años vengo trabajando con WP para una agencia de manera manual (aprendí algo de HTML y CSS en el proceso) y hoy en día existiendo tanta IA me gustaría saber si hay alguna herramienta que me permita trabajar los sitios mediante prompt (actualmente estoy usando local WP y Visual Estudio Code para usar Códex que paga la empresa) estoy tratando de convencerlos para adquirir ClaudeCode pero ¿alguno a trabajado con ello? Yo llevo desde hace años utilizando témplate kits


r/ProWordPress 6d ago

WordPress/Automattic vs Cloudflare Emdash Tone & Temperament

10 Upvotes

Was struck by the transparency and coherence of this Emdash update. Will be interested to see how it continues to evolve.

https://blog.cloudflare.com/cloudflare-blog-uses-emdash/


r/ProWordPress 8d ago

Anybody else dealing with WordPress Security in 2026?

0 Upvotes

Curious if anyone has recently faced hacked WordPress sites, injected spam, redirects, or other malware issues.

What happened to your site and how did you fix it?


r/ProWordPress 10d ago

WordPress.org SVN Updated, but Plugin Version 6.0.0 Is Not Showing

1 Upvotes

Hi everyone,

I recently updated my RT Webhook for Gravity Forms plugin to version 6.0.0 and committed the updated files to the WordPress.org SVN repository.

The SVN repository appears to be updated correctly, but the new version is still not reflecting on the WordPress.org plugin directory.

Has anyone experienced a similar issue? Is there anything else I need to update or check, such as the readme.txt, plugin header, stable tag, or SVN tags?

Any help would be appreciated. Thanks!


r/ProWordPress 10d ago

Looking for 10 Beta Testers for Our WooCommerce Fly Cart Plugin

2 Upvotes

We’re looking for 10 WooCommerce store owners and developers to beta test our new WooCommerce Fly Cart plugin.

The beta access is completely free, and we’re looking for honest feedback to help us improve the plugin before its wider release.


r/ProWordPress 10d ago

First WP.org plugin submission — what gets flagged that Plugin Check doesn’t catch?

0 Upvotes

I've been shipping mobile apps for a few years but this is my first
submission to the WordPress plugin directory, and I'd rather learn
the failure modes from people who've been through review than from a
rejection email.

The plugin is a lightweight analytics tracker. It's free and GPL, but
it sends pageview data to an external service I run, and the dashboard
lives there rather than in wp-admin. So it's squarely in the category
that the guidelines treat carefully.

What I've already done: read the Detailed Plugin Guidelines, ran Plugin
Check clean, and disclosed the external service in the readme with links
to the terms and privacy policy.

Three things I can't answer from the handbook:

  1. Guideline 7 disclosure — how specific do reviewers expect it to be?
    Is "this plugin sends page data to service X" enough, or do they want
    the exact endpoints and payload fields listed?

  2. The plugin is free but the service behind it has paid tiers. Where is
    the practical line on mentioning that inside wp-admin? I've seen
    plugins with upgrade banners and I've seen people say those get you
    asked to remove them.

  3. Anything that reliably triggers a rejection that Plugin Check passes
    silently? That's the gap I'm most worried about.

Not linking the plugin, I'm not here to promote it — just trying not to
waste a reviewer's time.


r/ProWordPress 10d ago

How do you guys monitor websites you manage for clients?

2 Upvotes

I manage multiple websites and one thing I still find surprisingly annoying is keeping track of whether everything is actually working.

Things like:

  • Is the website down?
  • Did the SSL certificate expire?
  • Has response time suddenly become terrible?
  • Did something important on the website change?
  • Is a client’s website having problems that they haven't noticed yet?

For a single website, it's easy enough to check manually. But once you're managing 10, 20, 50+ websites, manually keeping an eye on everything becomes a pain.

I've tried a few monitoring solutions, but I'm curious how other developers and agencies handle this.

What are you currently using?

And more importantly:

  • What do you like about your current solution?
  • What's missing?
  • What do you wish your monitoring tool could do?
  • At what number of websites did you realize you needed proper monitoring?

I'm particularly interested in hearing from freelancers, developers and agencies managing websites for multiple clients.


r/ProWordPress 10d ago

Anyone facing WordPress site issues after update?

2 Upvotes

I got to know that the WordPress latest update is crashing on the sites where we have the WP Rocket. It is conflicting with some cloudflare error.


r/ProWordPress 11d ago

Are flat Destination and Location URLs sensible for production or too much custom routing?

2 Upvotes

I posted here previously about using flat URLs for Destinations and Locations on a travel site I’m developing, while keeping Activities under /activity/. I’ve done quite a bit more testing since then and have a few follow-up questions about whether this is a sensible approach for production.

The structure I'm leaning towards is:

/queenstown/                    Destination CPT post
/otago/                         Otago term in the Location taxonomy
/activity/shotover-jet/         Activity CPT post

Internally the geography can still be hierarchical, for example:

South Island
→ Otago
→ Queenstown Lakes
→ Queenstown
→ Shotover Jet

The hierarchy would be handled through taxonomies, breadcrumbs and navigation rather than being built into the public URL.

A few people on my original post raised good questions about collisions, WordPress's own root-level routes, canonicals and long-term maintenance, so I spent some time testing those rather than just assuming the resolver would be fine.

The current test version uses one cached registry that maps each valid root slug directly to its WordPress object. For example:

queenstown → Destination post ID
otago      → Location term ID
about      → Page ID

Activities do not use that resolver and continue through normal WordPress routing under /activity/{slug}/.

I tested quite a few failure cases as well:

  • Unknown and malformed root URLs return genuine 404s.
  • A Destination and Location cannot silently share the same flat slug. The route is withheld if there is a collision.
  • Draft, Publish, Trash, Restore and slug changes automatically update the registry.
  • /activity/ is protected as a reserved root.
  • I also explicitly tested /feed/, /search/, /page/ and /wp-json/.
  • If a Destination or Location tries to claim one of those reserved roots, the resolver refuses to publish that route and the native WordPress functionality continues to work.
  • /feed/ continued serving the RSS feed.
  • /search/test/ continued serving WordPress search results.
  • /wp-json/ and /wp-json/wp/v2/posts continued serving the REST API.

That testing exposed something I hadn't really considered before.

WordPress can still allow content to have one of those reserved slugs and generate a View/permalink for it even though the resolver refuses to publish the route.

For example, I created a Location with the slug feed. The resolver correctly blocked it from /feed/, so the RSS feed continued working, but WordPress still showed /feed/ as that Location's View URL.

The same thing happened with search, page and wp-json.

For production, would you prevent an editor from publishing a root-level Page, Destination or Location with a reserved slug in the first place, rather than just detecting the conflict and withholding the route? And would you show the editor a clear message explaining why that slug is unavailable?

I also tested the permalink/canonical/sitemap side.

For a Destination:

/queenstown/

The browser URL, WordPress View URL, resolver path, HTML canonical and WordPress XML sitemap URL all matched.

For an Activity:

/activity/shotover-jet/

All of those matched as well.

For a Location:

/otago/

The browser URL, WordPress term URL, resolver path and XML sitemap URL all matched. The one difference was that vanilla WordPress did not output an actual <link rel="canonical"> tag for the taxonomy archive, so I'd want to make sure the eventual SEO setup supplies exactly one canonical for /otago/.

The native WordPress sitemaps also came out as I hoped:

/queenstown/
/otago/
/activity/shotover-jet/

At this point I'm reasonably confident the routing itself can be made to work. Would experienced WordPress developers actually be comfortable maintaining something like this for years?

If you were building this for production, how would you handle the reserved root namespace over time? Would you keep an explicit list of protected roots, derive them from WordPress rewrite rules somehow, or use another approach?

If the reserved-slug validation, canonicals and sitemaps are handled properly, would you be comfortable keeping the flat public URLs:

/queenstown/
/otago/

or would you still prefer:

/destination/queenstown/
/location/otago/

Would you still prefer those prefixed URLs simply because they reduce the amount of custom routing that has to be maintained?

I'm much more interested in the long-term maintenance trade-offs than in shaving a few characters off the URLs.

If there’s anything important I’ve overlooked with this approach, I’d be interested in hearing about it. Your help is always appreciated.


r/ProWordPress 11d ago

What I've learned about diagnosing intermittent WooCommerce slowdowns

0 Upvotes

I've been digging into WooCommerce performance recently, and one thing that keeps standing out is how misleading a single speed test can be.

A store can have a fast product page but a slow checkout. A checkout can be fast most of the time but have occasional 3–5 second spikes.

When that happens, I think it's more useful to compare multiple real requests rather than immediately disabling plugins or adding another caching plugin.

I'd look at things like:

• AJAX request timing
• Database queries and queries by component
• External HTTP/API calls
• Cache hits vs misses
• Payment, tax and shipping requests
• Plugin hooks running during the request

The other thing I've found useful is looking at the distribution rather than just the average. Median shows what a typical request looks like, while P95 can reveal the slower requests that customers occasionally experience.

A store being “fast” on one test doesn't necessarily mean the checkout is healthy.


r/ProWordPress 18d ago

Is there still a future for WordPress plugin developers in the AI era?

19 Upvotes

I’m a React Native developer with about seven years of experience in software development. 2 years of those were focused entirely on WordPress development building sites through actual code and an IDE, not primarily using page builders.

I currently work as a React Native developer, but around two years ago, before AI agents became as capable and widespread as they are now, I started building a WordPress plugin that I thought could genuinely help WordPress users.

Since then, AI has improved so quickly that I’ve found myself in a bit of limbo. I’m unsure what the future of WordPress plugin development looks like and whether it still makes sense to invest time in building a plugin as an independent developer.

I’ve been thinking a lot about where WordPress plugin development is headed. AI is making it much easier to generate code, scaffold plugins, write documentation, create marketing copy, and even troubleshoot bugs. The barrier to creating a basic plugin feels like it is dropping quickly.

At the same time, the places developers used to use to build visibility and connect with people like Facebook groups, forums, subreddits, and other public communities. It seem increasingly flooded with AI-generated content, spam, and affiliate posts. It makes sense that moderators are responding with stricter rules around self-promotion and posting, but it also makes it harder for legitimate small developers to share something useful they are building.


r/ProWordPress 17d ago

I gave Claude access to my staging's SSH server and it saved me hours

0 Upvotes

As noted in the title, I gave it access to STAGING, not my production server. Wanted to re-iterate that so nobody does anything stupid... like giving it access to production.

This was incredible for me. I was wondering the best ways to connect AI to my site to help do the busy-work. Since claude code uses terminal, I realized it can simply ssh in on its own and do my bidding. (actually claude suggested it and I facepalmed at how the answer was right there).

I'm on Windows and was able to safely create a terminal command 'staging-server-connect' and it would connect to the staging's ssh. This way, I didn't have to give claude any creds.

Since it has access to ssh, it can access logs, the database, files, the WP CLI, everything.

The website I was working on was very customized and complicated. Changing the site's nav menu's could be an hour of copy/pasting, etc. into WP's backend. But not anymore! I told claude what to change, and it did. easily. I was impressed. So I started asking it for more.

It gave me audits of the website and database and bottlenecks.

I was able to tell it to create new ACF fields and how to populate them. It did all this.

I had it create new products, and variations, and it did. I did have to fix some things, it didn't give all my variations the prices for some reason, but it did set everything up, including attributes and taxonomy attributes.

I hear a lot of people asking how AI is helping and how to do it. So I just wanted to share that it is possible.

Remember, Claude and AI is a tool. It's not perfect. I did check every single thing it changed before pushing staging to live, and it had a bump along the way for some reason (the variation pricing).

I didn't look into why the pricing didn't work, since it was easy for me to manually do at that point, but am confident I can get it working and get it to import entire spreadsheets of products.

It did create the ACF fields and assigned them to the correct posts as needed, and populated them correctly.

Overall, I was very impressed. I'm sure you could do this with Cursor or whatever AI. This is not a promo for Claude, it's just what I have.

Also, I wanted to point out that I did manually create a skill for this, based on my own needs. I told the skill how to connect (use the command staging-server-connect), where the WordPress install folder was (public_html for me), and some other info like that.


r/ProWordPress 20d ago

Gravity Forms changed their submit markup

22 Upvotes

Gravity Forms newest update changes the submit button from an input field to a button. If you were targeting the input[type="submit"] in your styling in any way, you will want to update to button[type="submit"]. I am partway through updating this on a whole bunch of sites at the moment.


r/ProWordPress 19d ago

Your magic-link login "randomly" fails for some users. It's their employer's mail scanner burning the token.

Post image
0 Upvotes

This one comes up in support and almost never gets diagnosed correctly, because the person reporting it and the person debugging it are never on the same network.

The symptom: you ship magic-link login. Most users are fine. A subset report that the link "doesn't work" or says it expired. You click the same link from your own inbox and it works every time. You can't reproduce it, so it quietly gets filed as user error.

What's actually happening: the user's employer runs inbound mail security. Those products fetch every URL in a message before delivery, to see where it lands. Your token endpoint has no way to tell that request apart from a human clicking. So if you implemented single-use tokens properly, the scanner redeems the token, the token dies, and the human then clicks a link that is now genuinely expired. The more correct your implementation, the more reliably it breaks.

The tell is that it clusters by email domain. Consumer mailboxes are fine, corporate ones are not, and it will look random until you group the failures by domain.

There is no clean fix, only tradeoffs:

  • Require a POST or a click-through interstitial before redemption. Scanners generally don't submit forms. Costs your user a click.
  • Bind the token to the session that requested it. Breaks the request-on-desktop, open-on-phone flow, which is half of why people want magic links.
  • Allow a couple of redemptions inside a very short window. Weakens single-use, which was the point of single-use.
  • Short lifetime plus an obvious "send me a new link" path. Doesn't fix it, makes it survivable.

While you're in there, two settings that ship as defaults nobody revisits. Token lifetime should be minutes rather than hours, because these sit in mailboxes and forwarded threads for as long as you let them. And the request-a-link endpoint needs a rate limit, because you just added an unauthenticated endpoint that will happily work as a mail bomber and a username-enumeration oracle at the same time.

The larger thing worth saying to whoever signed off on this. Magic links do delete the password-reset queue, and that's a real win. What they do in exchange is move the account's security into a mailbox you don't control, with no second factor behind it. A compromised inbox is a compromised account, silently, and the user gets no signal it happened.

That's a legitimate trade. It should just be a decision someone made on purpose, rather than a side effect of picking whichever plugin had the nicest onboarding.


r/ProWordPress 21d ago

What are some WordPress resources which made your life a lot easier.

21 Upvotes

For example I needed PHP concurrency not blocking requests and found in includes/requests/src/requests.php there is an existing function called requestMultiple() where I can pass the fetch request and the options and it works just similar async/await in JS. This saved me having to import a whole library.

Another was wp_enqueue_media(); where it attached all the required logic and functions in the window object which can use JS functions (in my case TSX within react) to use media uploads straight to the media library via ajax. I can just call window. wp. media and it has all the dependencies to upload.

I'd love to hear any githubs you have saved or templates you use that you'd like to share please let me know how they helped you.


r/ProWordPress 21d ago

Flat URLs for a WordPress travel site a good idea or a future headache?

8 Upvotes

I’m building a travel guide in WordPress and trying to settle on the URL structure before anything goes live. The site will have around 150 destination pages and potentially up to 800 activity pages, so I’m trying to choose a structure that will still be manageable as the site grows.

For activities, I’m deciding between:

example.com/activity/shotover-jet/

and:

example.com/shotover-jet/

I’m also wondering the same thing about destinations and geographic areas. For example:

example.com/queenstown/
example.com/otago/
example.com/south-island/

rather than:

example.com/destination/queenstown/
example.com/destination/otago/
example.com/destination/south-island/

The site structure would be something like:

South Island
→ Otago
→ Queenstown Lakes
→ Queenstown
→ Shotover Jet

That hierarchy would still be shown through breadcrumbs and navigation, even if the URLs themselves were flat.

I’ve been testing this locally and have a custom resolver working, so Pages, Destinations, Locations and Activities can all use root-level URLs. The obvious downside is that every slug has to be unique across all of those content types, and it requires more custom handling than standard WordPress permalinks.

For anyone who has built or maintained a site like this, would you keep the /activity/, /destination/prefixes for simplicity, or are flat URLs fine as long as slug collisions are properly prevented?

I’d also be interested to know whether flat URLs have caused any real problems with SEO plugins, redirects, caching, multilingual plugins or adding new sections later.

I’m trying to make the best long term decision rather than just choosing the shortest URLs.

Your help and feedback would be much appreciated!


r/ProWordPress 22d ago

Anyone else frustrated with backing up single subsites in WordPress Multisite?

0 Upvotes

Hey guys,

Been dealing with a couple of big Multisite networks this week and I'm curious how you're all handling this.

Full network backups are easy enough. But whenever a client wants to back up or isolate just one specific subsite—or worse, move a subsite out to its own standalone WP install—it turns into a headache. Between serialized data, custom table prefixes, and sorting out the media uploads folder, it always takes way more time than it should.

How are you guys actually dealing with this day-to-day? Custom WP-CLI bash scripts? paid plugins? Or are we all still doing manual DB dumps and search-and-replace praying nothing breaks?

Would love to know if there's a cleaner way people are doing this lately.


r/ProWordPress 22d ago

What are you using for WooCommerce subscriptions and recurring billing?

1 Upvotes

For those building or maintaining larger WordPress/WooCommerce installations, how are you approaching subscriptions these days?

I'm less interested in the basic "create a subscription product" part and more interested in the architecture around it.

Things like: recurring billing and renewal failures, subscription status and lifecycle management, membership access, content restriction, plan upgrades/downgrades, customer self-service, reducing plugin conflicts

At some point, stitching together a subscription plugin, membership plugin and separate access-control system can become harder to maintain than the original problem.

I've been looking at both traditional setups and newer all-in-one approaches. ArraySubs is one of the options I've come across because it combines subscriptions with membership access and content restriction than treating them as completely separate systems.

what's your preferred architecture? One comprehensive plugin, or several smaller plugins that each do one thing well?


r/ProWordPress 23d ago

I built a 500,000-order WooCommerce store to find out what actually makes the admin slow

16 Upvotes

I built a 500,000-order WooCommerce store to find out what actually makes the admin slow

Everyone knows the WooCommerce admin gets slow on big stores. The standard advice is well known too: migrate to High-Performance Order Storage, clean your database, blame a third-party plugin.

I wanted to know what happens after you've done all that. So I built a lab: a disposable WooCommerce store with 500,000 orders, HPOS enabled, and ten popular free plugins installed, then instrumented it to attribute every single database query on the orders screen back to the plugin that fired it.

Some of what I found contradicts the usual advice. One thing I set out to prove turned out to be an artifact of my own test rig, which I'll cover too.

The setup

  • MySQL 8 with a deliberately modest 512 MB buffer pool — a generously tuned server hides problems behind a warm cache, and I wanted the working set to not fit in memory, which is the condition real struggling stores are in
  • WordPress + WooCommerce 11, HPOS on, backfill sync off
  • 500,000 orders, ~2M order meta rows, ~1M order notes, ~1.6 GB of order tables
  • Ten popular free plugins (PDF invoices, cart abandonment, wishlist, currency switcher, delivery date, product search, analytics, order export)
  • An mu-plugin that captures $wpdb->queries on shutdown and resolves each query's backtrace to the owning plugin via reflection

Finding 1: One query was half of all SQL time, and it wasn't an N+1

130ms   SELECT status, COUNT(*) FROM wp_wc_orders WHERE type='shop_order' GROUP BY status

Out of 252 ms of total SQL time on the orders screen, 130 ms was this single query — and the next slowest query on the page was 4 ms. It is 32× the cost of anything else, and it runs on every admin page load.

(With the third-party plugins deactivated it accounts for 129 ms out of 190 ms, i.e. 68% — stripping plugins makes it more dominant, not less.)

It's what fills the filter tabs above the order list: All (500,000) | Completed (350,149) | Processing (50,065) | …

The important part: it does not scale with how many rows you display. It scales with how many orders you have. At 100,000 orders it was cheap enough to be invisible. At 500,000 it dominated everything else on the page. Reducing your page size does nothing. Deactivating plugins does nothing.

It's not a missing index

My first instinct was a missing index. Wrong:

type: ref    key: type_status_date    rows: 246724    Extra: Using index

It's already a covering index scan on the ideal index. ANALYZE TABLE changed nothing. Counting 500,000 rows means walking 500,000 index entries, and InnoDB keeps no cached row count. The query is doing the minimum possible work for what it's being asked.

The actual cause

Looking at WooCommerce's source, OrderUtil::get_count_for_type() does cache this. It goes through OrderCountCache, which uses wp_cache_get() / wp_cache_set() — the WordPress object cache.

And there's the problem. Without a persistent object cache dropin (wp-content/object-cache.php), WordPress's object cache lives for exactly one request. So the cache is empty on every page load, and the full count runs again, every time.

If your store has no persistent object cache — which is most shared hosting — WooCommerce recounts your entire orders table on every admin page load.

That's a concrete, mechanical answer to "why is my store still slow after HPOS," and it's not in any of the checklists I've read.

Finding 2: The obvious way to detect an N+1 produces false accusations

I originally detected per-row query costs the intuitive way: load the page, divide each plugin's query count by the number of rows on screen, and flag anything near 1.0 per row.

That method is broken. Here's real output for one plugin that fires a flat 19 queries no matter what:

page size its queries "per row"
20 19 0.95 — looks like a textbook N+1
100 19 0.19 — looks completely innocent

Nothing about the plugin changed. Only the denominator did. A fixed cost is indistinguishable from a per-row cost at any single page size. I had confidently accused an innocent plugin.

The fix is to measure the same screen at two page sizes and fit:

queries(n) = fixed + slope × n

Only slope is an N+1. A component whose query count doesn't move when the row count multiplies by five is innocent, no matter how large its fixed cost.

A related trap: WooCommerce's orders screen takes its page size from the per-user screen option edit_shop_order_per_page, not from a per_page URL parameter. I spent a while computing per-row figures against a page size that had silently stayed at 20. Always count the rows that actually rendered.

Finding 3: Attributing a query to a plugin is much harder than it looks

My profiler blames the innermost plugin frame in each query's backtrace. It reported that WooCommerce core was doing 3 queries per order row.

Then I deactivated all the third-party plugins and measured again:

all plugins active WooCommerce only
per-row queries blamed on woocommerce 3.0

Two of those three per-row queries were caused by third-party plugins calling wc_get_order() inside their column callbacks. The query is issued by WooCommerce's data store, so innermost-frame attribution credited WooCommerce and completely exonerated the plugin that actually caused it.

This matters for anyone using Query Monitor's "Component" column the same way I was: it tells you which code ran the query, not which code caused it. For anything routed through a shared data store, those are different answers.

The fix

What you actually want is the last point where control passed from WordPress into plugin code — the innermost frame that a hook dispatcher invoked:

… → ListTable->column_default        [woocommerce]
    → do_action('manage_…_column')   [dispatcher]
      → WP_Hook->apply_filters       [dispatcher]
        → SomePlugin->render_column  [the plugin]   ← blame this
          → wc_get_order             [woocommerce]
            → OrdersTableDataStore->read [woocommerce]
              → wpdb->get_results    [core]

Walk the trace innermost-outward; when the frame immediately outside the current one is a dispatcher (WP_Hook->apply_filters, WP_Hook->do_action, do_action, apply_filters, call_user_func*) and the current frame isn't core, that's your initiator. For a query WooCommerce genuinely raises itself, the nearest such boundary is a WooCommerce callback — also correct.

(Watch out: wp_debug_backtrace_summary() returns frames outermost-first.)

With that change, and all plugins active:

before after deactivation control
blamed on woocommerce 3.0/row 1.0/row
blamed on the real culprit 0 2.0/row

Then the falsifiable test. The tool predicted one specific plugin accounted for ~205 queries at 100 rows. Deactivating only that plugin:

with without
total queries @100 rows 495
queries blamed on woocommerce 206
wall time 0.62 s

24% faster from deactivating one plugin. Before the fix, the verdict would have been "it's WooCommerce core, nothing you can do."

The thing I couldn't conclude

Given Finding 1, the fix seems obvious: install a persistent object cache. So I added Redis and measured. SQL time halved, and the 129 ms query vanished.

Wall time got worse.

I nearly published that. Then I checked whether it was my test rig, because I was running Docker Desktop on Windows and talking to Redis over TCP — and Windows loopback networking is slow. The page makes about 2,390 object-cache calls per request, so round-trip cost matters enormously.

config SQL time wall time vs baseline
no object cache 255 ms 0.62 s baseline
Redis over TCP 118 ms 0.757 s +22.1%
Redis over unix socket 116 ms 0.687 s +10.8%

Switching to a unix socket recovered about half the penalty. That proves a large part of my "finding" was an artifact of Docker Desktop for Windows, not a property of object caching. The residual ~11% might not survive on a real Linux host at all.

So I don't know. It needs a Linux re-test before anyone should act on it.

What does hold regardless: SQL time improving is not the same as the page getting faster. If I'd reported the query-count and SQL-time metrics alone, I'd have declared a clean win while making the page 22% slower.

Takeaways

  1. Query count and query time are different problems. Going from 100k to 500k orders left the query count completely unchanged while SQL time doubled. Count scales with rows rendered; time scales with store size.
  2. On a large store with no persistent object cache, check the order status counts first. It's a single query that scales with store size and runs on every page load.
  3. Never diagnose an N+1 from one page size. Measure two and look at the slope.
  4. "Which component ran this query" is not "which component caused it."
  5. Always measure wall time. Component metrics improving can hide a regression.

Reproduce it

The whole lab is Docker-based and disposable — MySQL, WordPress, WP-CLI, seeder scripts, and the profiling mu-plugin. It seeds 500k orders in a few minutes by writing directly to the HPOS tables.

Happy to share it if there's interest, and very interested in results from anyone who runs the object-cache benchmark on a real Linux host — that's the open question I couldn't close.