The Problem
I ran into this on a client store the day they pushed WooCommerce 10.7 to production. The shop, the cart, and the checkout all worked fine. The /my-account/ page itself loaded. The moment a logged-in customer clicked any tab (Orders, Downloads, Edit address, Payment methods) they got a hard WordPress 404.
The URLs looked right in the navigation. /my-account/orders/ rendered the theme's standard 404 template. /my-account/edit-address/ did the same. The header redirect on logout still routed back to /my-account/, so the parent page was registered. Only the endpoint children were broken.
The error was identical in an incognito session after a fresh login, identical for the shop owner with manage_woocommerce, and identical with every plugin except WooCommerce deactivated. A Settings > Permalinks > Save round trip fixed it for about ten minutes, then the 404s came back the next time anyone touched the role editor plugin or saved a theme option.
Why It Happens
WooCommerce 10.7 finished a refactor of how the My Account endpoints get registered with the rewrite API. In 10.6 and earlier, add_endpoint() ran on init at priority 10. In 10.7, it moved to priority 100 so that the registration happens after HPOS bootstraps and after any post type that other plugins add on the default init hook.
The new priority is the right call. The side effect is that anything which flushes rewrite rules early — and there are more of these than you would think — now flushes the rules before WooCommerce has registered the endpoints, leaving the rewrite array without them until the next flush.
The usual culprits, in order of how often I see them:
- Role editor plugins. Plugins that touch capabilities on
initpriority 5 sometimes callflush_rewrite_rules()when they save a role. In 10.6 that was harmless because the endpoints were already registered at priority 10. In 10.7 the flush captures an empty endpoint list and overwrites the databaserewrite_rulesoption. - Theme option frameworks. Redux, Kirki, and several premium theme dashboards call
flush_rewrite_rules()when you save a custom post type setting. Same problem. - Caching plugins with "permalink helpers". A few page-cache plugins flush rewrites after a permalink change to invalidate their internal URL map. If they hook anywhere before
initpriority 100, the same race condition fires. - Capability mismatches. Even with the rules in place, HPOS gates the orders endpoint on
read_shop_order. If a role editor stripped that capability from customers (some templates do this thinking it is an admin-only cap), the orders tab loads a permission-denied state that the theme renders as a 404.
The WooCommerce 10.7 release notes mention the endpoint refactor in passing. The My Account endpoints documentation covers the API but not the priority change.
The Fix
You need to do three things: confirm the rules are flushed at the right time, stop the early-flush culprit from clobbering them, and restore the read_shop_order capability on the customer role.
Step 1: Force a clean flush on a late hook. Drop this into a small mu-plugin (wp-content/mu-plugins/woo-endpoint-fix.php):
<?php
/**
* Plugin Name: Woo Endpoint Fix
*/
add_action('woocommerce_init', function () {
if (get_option('woo_endpoints_flushed_107') !== '1') {
WC()->query->init_query_vars();
flush_rewrite_rules(false);
update_option('woo_endpoints_flushed_107', '1');
}
}, 100);
woocommerce_init fires after WooCommerce has registered all endpoints. flush_rewrite_rules(false) rewrites the database option without nuking the .htaccess file, which avoids breaking any custom rules above the WordPress block. The option flag stops it from running on every request. Load any admin page once after deploy and the rules go in.
Step 2: Identify the early flusher. If endpoints break again after a settings save, find the offender:
add_action('flush_rewrite_rules_hard', function () {
error_log('rewrite flush from: ' . wp_debug_backtrace_summary());
});
add_action('flush_rewrite_rules', function () {
error_log('rewrite flush from: ' . wp_debug_backtrace_summary());
});
Trigger a save in the suspected plugin, then open wp-content/debug.log. The backtrace points straight at the file and function calling flush_rewrite_rules(). From there, two options: patch the plugin's hook to run on wp_loaded instead of init, or wrap your own flush right after it.
Step 3: Re-register the orders capability. If the rewrite is in place but /my-account/orders/ still 404s for customers, run this once:
add_action('admin_init', function () {
$customer = get_role('customer');
if ($customer && ! $customer->has_cap('read_shop_order')) {
$customer->add_cap('read_shop_order');
}
});
The capability is read-only, so this is safe to ship. It restores the access check that HPOS uses to render the orders list. If you still see 404s on a single user but not on others, their saved wp_capabilities user meta is stale; a role save in the admin rewrites it.
Step 4: Verify with curl, not the browser. Browsers cache 404 responses aggressively, and so do most CDNs. From the server:
curl -I -b "wordpress_logged_in_xxx=..." https://example.com/my-account/orders/
A working endpoint returns HTTP/2 200. A 404 with a cache-control: max-age=... header means a layer above WordPress is still serving the old 404; purge the CDN for /my-account/* and try again.
The Lesson
WooCommerce 10.7 shifted endpoint registration to init priority 100 to play nicely with HPOS, and the side effect is that any plugin flushing rewrite rules at priority 10 or earlier wipes them out. Flush once on woocommerce_init, find and reorder the early flusher, and restore the customer's read_shop_order cap so the orders tab loads.
If your store is one of the ones where the upgrade looked clean but the customer area quietly broke, that is the kind of cleanup I do. See my services. For a related WooCommerce 10.7 admin gotcha, see WooCommerce 10.7 short description editor missing in admin.