WooCommerce documents that woocommerce_should_register_blocks runs on plugins_loaded before the main query is parsed. Conditions based on is_page, is_product, queried objects, or late route state can return the wrong answer. Pretty REST paths, rest_route query URLs, cron, admin-ajax, WP-CLI, webhooks, and custom callbacks also expose different early signals. A condition that is too broad can add block registration cost to every API request.
Use this when an extension must opt into WooCommerce block registration for one non-rendering request and needs a condition that works before WordPress builds the main query.
Quick answer
Create a table of real requests and record REQUEST_METHOD, REQUEST_URI, rest_route, DOING_CRON, DOING_AJAX, REST_REQUEST, WP_CLI, authentication, callback, and expected rendering need at plugins_loaded. Do not use main-query helpers in the filter. Match a stable owned route or explicit query flag, normalize the path, reject ambiguous values, and return the existing decision for every unrelated request. Add a temporary redacted log on staging to prove the matrix, then remove it.
Test scenarios to run
Run the same controlled fixture across these branches. Write down the expected result before testing so a surprising response is easy to identify.
| Scenario | Fixture | Expected result |
| Pretty REST | /wp-json/acme/v1/card | Match owned path only |
| Query REST | ?rest_route=/acme/v1/card | Normalize and match |
| Cron | DOING_CRON true | Keep skipped unless owned |
| Frontend | Product page | Normal registration path |
Diagnostic table
Use this table to connect the observed behavior to evidence and a verification step.
| Action | Evidence | Verification |
| Capture early request signals | Collect pretty-permalink REST, rest_route query, cron, AJAX, webhook, WP-CLI, frontend, admin, and editor fixtures. | Pretty and query-form owned routes opt in consistently. |
| Define the rendering contract | Record which constants, server fields, and GET values exist when plugins_loaded runs rather than assuming later WordPress state. | Cron, AJAX, REST, CLI, webhook, frontend, and admin controls follow the matrix. |
| Normalize the owned route | Define one explicit owned rendering contract and document why block registration is required in that request. | Unrelated requests retain the WooCommerce 11.1 performance behavior. |
| Return the prior decision elsewhere | Test encoded paths, duplicate slashes, subdirectory installs, reverse-proxy prefixes, query order, empty rest_route, and malicious lookalikes. | Temporary logs are removed and no sensitive headers or payloads were retained. |
What to check first
- Collect pretty-permalink REST, rest_route query, cron, AJAX, webhook, WP-CLI, frontend, admin, and editor fixtures.
- Record which constants, server fields, and GET values exist when plugins_loaded runs rather than assuming later WordPress state.
- Define one explicit owned rendering contract and document why block registration is required in that request.
- Test encoded paths, duplicate slashes, subdirectory installs, reverse-proxy prefixes, query order, empty rest_route, and malicious lookalikes.
- Compare registration state, response output, timing, SQL, memory, and logs for matched and unmatched fixtures.
Field notes
- Write the expected result before changing anything and keep one repeatable synthetic fixture for the full test window.
- Record exact versions and UTC timestamps because caches, retries, scheduled actions, and deployments can change the evidence between checks.
- Test the public path and the stored server-side result, not only an admin preview, isolated command, or API response.
- Review the result again after the relevant cache, queue, cron, webhook, and observation window has completed.
Useful command or data shape
Adapt paths, IDs, and privacy handling to the site before running commands or storing data on production.
add_filter( 'woocommerce_should_register_blocks', function ( $should ) {
$path = parse_url( $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH ) ?: '';
$route = isset( $_GET['rest_route'] ) ? '/' . ltrim( sanitize_text_field( wp_unslash( $_GET['rest_route'] ) ), '/' ) : '';
return in_array( $path ?: $route, [ '/wp-json/acme/v1/card' ], true ) ? true : $should;
} );
Why this usually happens
- The filter runs before conditional query tags know the requested object.
- A reverse proxy or subdirectory changes the URI shape used by a string match.
- An empty rest_route or broad substring matches unrelated API calls.
- Temporary diagnostics are left active and log authorization or personal request data.
Decision rule
The filter may return true only for a request your extension owns and can identify at plugins_loaded. If the condition needs the main query, move the rendering design instead of guessing early.
Production verification checklist
- Pretty and query-form owned routes opt in consistently.
- Cron, AJAX, REST, CLI, webhook, frontend, and admin controls follow the matrix.
- Unrelated requests retain the WooCommerce 11.1 performance behavior.
- Temporary logs are removed and no sensitive headers or payloads were retained.
Safe fix order
Use a sequence that makes each result easy to prove. Stop when new evidence changes the scope or owner of the problem.
- Capture early request signals
- Define the rendering contract
- Normalize the owned route
- Return the prior decision elsewhere
- Remove diagnostics after proof
Mistakes to avoid
- Changing production before recording exact versions, UTC timestamps, a stable fixture, the expected result, and a tested rollback point.
- Treating one successful screen as proof while logs, stored records, background jobs, caches, emails, APIs, and downstream systems remain unchecked.
- Testing only as an administrator instead of using the role, device, locale, cache state, request path, and failure branch that users actually reach.
- Leaving debug output, temporary exclusions, helper accounts, duplicate hooks, broad permissions, or relaxed firewall rules active after verification.
Questions teams ask during testing
Can I test this directly in production?
Start with read-only evidence. Use staging for code, package, security, checkout, form, privacy, or cache changes. If a production canary is necessary, make it identifiable, reversible, monitored, and incapable of exposing personal data or charging a customer.
How do I avoid a false positive?
Repeat the same fixture with the same versions, URL, role, locale, cache state, and downstream integration. Compare the public result, stored result, and logs instead of relying on one browser view.
What evidence should I retain?
Keep UTC time, exact versions, request or record ID, expected result, actual result, relevant log lines, change made, rollback point, owner, and final verification. Redact credentials, tokens, and personal data.
When is the work complete?
Close it when the primary path passes, failure branches are understood, stored and downstream records reconcile, temporary changes are removed, monitoring is active, and the owner has the evidence packet.
What to tell the client or owner
Give the owner a concise packet with the affected workflow, exact versions, UTC test time, synthetic fixture ID, expected result, actual result, key logs, change made, rollback point, final result, unresolved risks, owner, and next review date. Remove credentials and personal data before sharing it.
When HandL WP should help
Bring in help when this affects leads, checkout, search visibility, security, paid media reporting, or a client production site. HandL WP can trace the issue through WordPress, hosting, cache, tracking, and Search Console, then verify the workflow after the technical fix.
If this is active on a production site, have HandL WP test WooCommerce request contexts.
Related HandL WP guides
Use these related guides when the same issue touches tracking, security, checkout, or crawler visibility.
Helpful references