xSpeed Cache is officially live! Get 50% OFF during Launch Week — Lifetime starts at just $79 Lifetime from $79 — 50% OFF, Launch Week! See Plans →

See Plans →
Get Plugin xSpeed Hub
xSpeed Hub Pricing Docs Blog Scan
Appearance
Get Plugin
All articles
Render BlockingResearchMethodologyPerformanceWooCommerceWordPresscheck:A1fix:render-blocking

Site Kit by Google Turns Off WordPress Defer on 92% of the WooCommerce Stores That Run It

16 min read
xSpeed Cache data study cover reading WooCommerce asked for defer, core said no, with Google, WooCommerce and WordPress logos and the figure 92.3% of stores running both

Updated October 2026

On 24 October 2026 we read the homepages of 2,363 WordPress origins from our own xSpeed Scan archive and counted 6,305 requests for a script loading strategy. WordPress granted most of them and refused 1,156, across 401 stores. The comparison people expect from a render-blocking study is one plugin against another. The more useful comparison is the same plugin against itself: wc-jquery-blockui was deferred on 309 of the stores that asked and blocking on 224 of them, same handle, same WooCommerce 11.2.1, the current release per wordpress.org’s plugin API on 24 October 2026. Something outside that plugin decides which it gets. This study names it.

Quick Summary: What to Check Before You Touch Another Setting

If you want to…Do thisWhy
Know whether core refused youLook for data-wp-strategy with no defer on the tagThat attribute is the only trace of a refusal
Find the causeList the handles that depend on yours, not the ones yours depends onCore walks dependents, in that direction only
Fix a WooCommerce store running Site KitCheck Site Kit’s WooCommerce conversion events before anything elsePresent on 82.8% of refusals, 2.5% of successes
Stop wasting a passDo not re-apply strategy to the refused handleCore will refuse it again for the same reason
Check the whole estateRun the free scan, then read the HTML of the worst pagesCheck A1 sizes the blocking in milliseconds

Three findings, in order of how much they should change what you do. Core refused 18.3% of all the strategies it was asked for. In 1,123 of 1,156 refusals the cause sits in a handle other than the one wearing the attribute. And on the stores where Google’s Site Kit registers its WooCommerce conversion-event script, 92.3% carry at least one refusal, against 40.0% of WooCommerce stores without it.

The Method, and Everything It Cannot Tell You

The method comes before the numbers here, because every number below is only as good as it.

The receipt. WordPress writes data-wp-strategy on a script tag whenever that handle asked for a loading strategy. When core grants the request the tag carries both the attribute and a real defer or async. When core withdraws it, the attribute stays and the real one never appears. Our earlier piece on render-blocking CSS and JavaScript established that behaviour and demonstrated it on ten sites. A refusal is therefore readable from outside the site, with no plugin installed and no login.

The corpus. We drew every origin the scan archive marks as WordPress, the same corpus behind our three-way count of caching plugin adoption, 2,582 of 3,736 scans across 150 pages of /api/scan-list, and requested each homepage once under a desktop Chrome user agent. 2,328 answered 200. 2,363 returned a document we could parse, because several 403 and 5xx responses still render a WordPress page. From each we kept only the opening <script> tags, which is enough to see every handle, every strategy attribute and every inline script printed in the after position.

The two refusal rules. Core’s filter_eligible_strategies() in wp-includes/class-wp-scripts.php, fetched from the WordPress develop repository on 24 October 2026, returns an empty list of eligible strategies in exactly two cases: a non-alias handle with no intended strategy of its own, and a handle with an inline script attached in the after position. It then recurses over the handle’s dependents, so an empty list anywhere below propagates all the way up. One of those two cases is visible in the HTML, because an after inline prints as <script id="handle-js-after">. The other is not.

Six limitations, before a single result

  • A homepage is one URL, logged out, at one moment. A store whose checkout refuses a defer and whose homepage does not is counted clean here.
  • We see only handles whose developer asked for a strategy in the first place. A plugin that never calls the API cannot appear, however much it blocks.
  • Cohort membership is read from the same HTML, so a plugin that is installed but prints nothing on the homepage counts as absent.
  • The population is stores that have been scanned by us, which is not a random sample of WordPress. It over-represents sites whose owner was already worried about speed.
  • For the 97.1% of refusals caused by a dependent, the dependency graph is not in the HTML. We can name a culprit by association and then confirm it in source, which we do below, but a single page cannot prove it on its own.
  • An optimizer that combines or minifies JavaScript destroys the file path we attribute by. 125 refused tags came through one: Jetpack Site Accelerator on 55, LiteSpeed Cache on 52, our own xSpeed Cache minifier on 9, Autoptimize on 4, and 5 we could not name. Rewriting the URL does not cause the refusal, and our own plugin does nothing to repair one either.

Result 1: One Store in Six Carries a Refusal, and Almost None of Them Are Self-Inflicted

Of the 2,363 origins parsed, 1,257 asked core for a strategy somewhere on the page, which is 53.2%. Of those askers, 401 got at least one refusal, which is 31.9% of the stores that tried and 17.0% of every WordPress store measured. The median affected store carries 3 refusals and the worst carries 17.

MeasureCountShare
WordPress origins parsed2,363—
Origins printing data-wp-strategy at all1,25753.2% of parsed
Origins with one refusal or more40117.0% of parsed
Strategy requests seen6,305—
Requests refused1,15618.3% of requests
Refusals caused by a dependent1,12397.1% of refusals
Refusals caused by the handle’s own after inline332.9% of refusals

The last two rows are the ones that decide what you do next. Only 33 refusals in the whole corpus are the fault of the handle in the tag, and 21 of those are a single pattern: a Cloudflare Turnstile widget loaded with its configuration printed directly after it. Everything else was refused because of something further down the tree.

Result 2: The Ranking Names WooCommerce, and WooCommerce Is Not the Culprit

Attributing each refused handle to the plugin or theme that registered it gives a ranking with one obvious leader. WooCommerce owns 937 of the 1,156 refusals, 81.1%, spread over 270 of the 401 affected stores.

Owner of the refused handleStoresRefusalsOf which self-inflicted
WooCommerce2709372
Cloudflare Turnstile323221
WordPress core2424—
Google Tag Manager2323—
Hostinger Reach2020—
Trustindex1212—
Jetpack Stats1119—
GTM4WP710—
Site Reviews777
MonsterInsights77—
XStore theme66—

Read that table as a list of victims, not of offenders. The refusal rate per handle is what gives it away.

Refusal rate by handle: wc-jquery-blockui refused on 224 of 533 stores that asked, wc-js-cookie 162 of 535, woocommerce 145 of 539, and core's wp-hooks 16 of the 18 origins that asked

wc-jquery-blockui asked for a strategy on 533 stores and was refused on 224 of them, 42.0%. The other 58% run the same plugin at the same version and get their defer. A handle refused everywhere it asks would point at a fault inside its own plugin. A handle refused on two stores in five points at something that varies between stores, which means a third party. The one row with the opposite shape is core’s own wp-hooks, refused on 16 of the 18 origins that asked for it, and that sample is far too small to carry a claim.

Result 3: The Differential Test Names Site Kit by Google

Comparing a plugin’s installed base against a refusal rate tells you very little, because almost everything correlates with running WooCommerce. The test that works holds WooCommerce constant and splits on core’s answer instead.

The differential test. Take every store where one handle carried data-wp-strategy and no defer. Take every store where the same handle carried both. Both cohorts asked. Only the answer differs. Now rank every other handle on the page by how much more often it appears in the refused cohort than in the deferred one.

For the woocommerce handle that gives 145 refused stores against 394 deferred, and one row runs away with it.

The differential test: googlesitekit-events-provider-woocommerce present on 82.8% of the refused cohort against 2.5% of the deferred cohort, with google_gtagjs marked as a confounder

googlesitekit-events-provider-woocommerce is present on 120 of the 145 refused stores, 82.8%, and on 10 of the 394 deferred stores, 2.5%. That is a gap of 80.2 percentage points. Turned round the other way, the test is cleaner still: 130 stores in the corpus print that script, and 120 of them, 92.3%, carry at least one refusal. Among the 412 WooCommerce stores without it, 165 do, which is 40.0%.

The Receipt: Two Registrations and One Loop in Core

Association is where this study would normally stop. It does not have to, because both halves of the mechanism are open source and both can be read on the day.

Site Kit by Google 1.190.0, released 19 October 2026 according to wordpress.org’s plugin API, registers the script in includes/Core/Conversion_Tracking/Conversion_Event_Providers/WooCommerce.php:

$script = new Script(
    'googlesitekit-events-provider-' . self::CONVERSION_EVENT_PROVIDER_SLUG,
    array(
        'src'          => $this->context->url( 'dist/assets/js/googlesitekit-events-provider-woocommerce.js' ),
        'execution'    => 'defer',
        'dependencies' => array( 'woocommerce' ),
    )
);

Two things in that array matter. dependencies makes the script a dependent of WooCommerce’s woocommerce handle, which puts it inside the tree core walks. execution is Site Kit’s own key, not WordPress’s, and its Script::register() records what happens to it:

wp_register_script( $this->handle, $src, (array) $this->args['dependencies'], $version, $this->args['in_footer'] );

if ( ! empty( $this->args['execution'] ) ) {
    wp_script_add_data( $this->handle, 'script_execution', $this->args['execution'] );
}

The fifth argument to wp_register_script() is a plain boolean. Since WordPress 6.3 that argument can instead be an array carrying strategy, and the 6.3 developer note states that this is how a script asks core to defer it. Site Kit does not use it. It adds its own script_execution key and appends the defer attribute later through a script_loader_tag filter, which works perfectly well for its own tag.

From core’s point of view the handle is registered, enqueued, has a src, and has no intended strategy. That is the first of the two refusal rules, and the result propagates up the tree.

How one dependent cancels the defer on six scripts above it, with the two return array() lines from filter_eligible_strategies

Nothing here is a bug report against Site Kit’s own loading. Its script defers correctly. The cost lands on the handles it declares a dependency on, and those belong to someone else. Four more of its conversion providers declare the same kind of cross-plugin dependency: Easy Digital Downloads on edd-ajax, Mailchimp for WordPress on mc4wp-forms-api, Popup Maker on popup-maker-site, and Ninja Forms on core’s own backbone. The Contact Form 7, WPForms, OptinMonster and content-event providers declare none. No handle belonging to any of those five dependency targets turns up in our refusal set at all, so the mechanism traced here is confirmed for WooCommerce and merely predicted for the rest.

The Confounder That Would Have Named the Wrong Plugin

One after inline shows an even larger association than the provider script, and following it would have produced a different and wrong article. google_gtagjs carries an after inline on 85.5% of the refused cohort and 13.2% of the deferred one. It is a tempting culprit: an after inline is one of core’s two refusal rules, and the lift is 72.3 points.

It is not the cause. google_gtagjs is not a dependent of any WooCommerce handle, so core never reaches it while deciding woocommerce. It scores highly because the same plugin installs both, which makes it a marker of Site Kit rather than a mechanism. The second-placed row, googlesitekit-events-provider-content-events at 77.9% against 2.5%, fails the same way: it ships in the same plugin and declares no dependencies at all.

That is the methodological point worth taking from this study. Ranking after inlines by association names the loudest co-occurring script. Only the dependency declaration explains anything, and the only way to separate the two is to read the registration.

What the Other 250 Stores Are Doing, and Why We Will Not Guess

Site Kit’s conversion providers appear on 133 of the 401 affected stores, 33.2%. A further 18 are fully explained by an after inline attached to the refused handle itself. That leaves 250 stores, 62.3% of the affected population, with no named culprit at all. The honest summary of this study is that it explains a large minority.

We ran the same differential across those 250 looking for a second offender and did not find one. Every high-lift row in that cohort is simply another WooCommerce handle, which says the stores run WooCommerce and nothing more. The single candidate worth recording is woo-variation-swatches, present on 10.1% of the unexplained refusals against 0.4% of a clean comparison cohort. That is a 27-store signal and we have not read its registration, so it is a lead rather than a finding.

Three things would close the gap and none of them can be done from a homepage: the registered dependency graph, which needs wp_scripts() on the server; a logged-out crawl of more than one template; and a controlled install where providers are switched on one at a time. The last is the one we would run next.

Running This Across the Stores You Did Not Build

On one site the differential is unnecessary, because the dependency graph is right there in wp_scripts()->registered. The problem is an estate, where the refusal shows up on the three stores whose owner complains and not on the thirty that never mention it.

xSpeed Hub, our fleet dashboard, runs the scan across every connected site, so the stores whose critical path is worst surface without opening thirty dashboards. WooCommerce stores are the ones to check first on this particular fault, since 937 of the 1,156 refusals land on WooCommerce handles. xSpeed Cache is ours, built by WPDeveloper, and on this fault it is a measuring instrument rather than a cure: nothing in our minifier restores a strategy core has withdrawn, and our combined files appear in 9 of the refused tags above.

Worth keeping in proportion before you spend a day on it. Our measured fix-priority study put page weight ahead of the critical path on most sites, and our scan of 958 origins found 83.9% of sites with a sub-200ms server response still failing LCP. A refused defer is real and it is cheap to find. It is rarely the largest thing wrong.

Run the free scan on your own site: xspeedcache.com/scan/

Common Mistakes People Make Chasing a Refused Defer

  • Re-applying strategy to the handle that shows the attribute. Core refused it for a reason that lives somewhere else and will refuse it again.
  • Reading the dependency list the wrong way round. Core walks dependents, the scripts that depend on yours.
  • Blaming the optimizer because the URL in the tag belongs to it. 125 refused tags were rewritten by one, and in every case the handle had already been refused.
  • Testing while logged in, where a different set of scripts loads and conversion tracking often does not fire at all.
  • Treating an after inline on a co-occurring script as the cause. It has to be a dependent before it can block anything.
  • Deferring everything with a plugin setting after core refused, which produces the breakage described in the post on a delayed script that broke a menu.

Frequently Asked Questions

I set strategy to defer and the tag came back with no defer at all. What did I do wrong?

Probably nothing. Check whether the tag carries data-wp-strategy. If it does, core received your request and withdrew it, which means a handle that depends on yours is ineligible. Fix that handle.

I removed every after inline from my own script and it is still blocking. Why?

Because the after rule applies to dependents too, and so does the empty-strategy rule. In our corpus only 2.9% of refusals came from the handle’s own inline. The overwhelming majority came from further down.

I uninstalled Site Kit and my defer came back. Is that proof?

For your store, yes, and that is the right test to run. It is also the experiment this study could not run at scale, which is why we published the association and the source side by side rather than one of them alone.

Does this mean Site Kit by Google is slowing my site down?

Not directly. Its own script defers as intended. The side effect is that WooCommerce’s scripts lose a deferral they had asked for, and on a product page that is usually several files moving back into the critical path.

I only run Site Kit for Search Console. Do I still get this?

Only if conversion tracking is on and WooCommerce is active, because the provider’s own source notes that it activates on class_exists( 'WooCommerce' ). The script printing on your page is the thing to check, not the setting.

My handle is not in your ranking. Should I assume it is fine?

No. The ranking is built from stores we have scanned, and it only sees handles whose developer asked for a strategy. A plugin that never calls the API blocks just as hard and leaves no receipt.

Why is the refusal rate 42% on one WooCommerce handle and 12% on another?

Because each handle has a different set of dependents, and the chance that one of them is ineligible rises with the size of that set. wc-jquery-blockui sits under more of WooCommerce’s tree than wc-cart-fragments does.

Can a caching or minification plugin fix this?

No, and ours does not. The decision happens in PHP while the tag is printed. Combining the files afterwards changes which URL appears, not whether core granted the strategy.

What is the fastest manual check on one store?

View source logged out, search for data-wp-strategy, and note every match that has no defer or async beside it. Each one is a refusal. Our guide to CSS and JavaScript options covers what to do about them once you have the list.

Is any of this visible in a PageSpeed report?

Only as a consequence. Chrome’s own Lighthouse documentation states which resources count as render-blocking, and a refused script simply appears as one of them with no explanation attached.

Does the same thing happen to stylesheets?

There is no stylesheet equivalent, which is the more serious half of the problem. WordPress gives scripts a dependency-aware API that refuses unsafe requests and gives stylesheets nothing at all, as our render-blocking guide sets out.

How often should this be re-measured?

Quarterly. Both Site Kit and WooCommerce ship frequently, and a single line in either registration would move every number in this study.

Conclusion: Fix the Dependent, Not the Handle

If you are…Do this firstThen
A store owner with one siteSearch the source for data-wp-strategy with no deferDisable conversion tracking in Site Kit and re-check
An agency with an estateScan the estate and sort by render-blocking millisecondsRun the differential on the two worst stores
A plugin developerPass strategy inside the fifth argument of wp_register_script()Audit anything you declare a dependency on
Comparing caching pluginsRead the 80-capability comparisonCheck Free vs Pro for what each tier automates

What to do this week: read the HTML of your busiest product page logged out and count the refusals. If Site Kit’s WooCommerce provider is on the page, turn conversion tracking off for one deploy and count again. Pass strategy properly in anything you register yourself. Then run the free xSpeed Scan and look at check A1, which sizes the blocking in milliseconds rather than counting files. xSpeed Cache is free forever for the complete toolkit, and Pro starts at $29 a year with a 14-day money-back guarantee if you want the tuning run for you, with the current pricing on the site.

If you run this differential on your own estate and find a culprit we did not name, we would rather hear about it than guess at the other 250 stores for another quarter.

Make your site load in milliseconds.

One switch. Zero bloat. Always free to start.