Commit 1544cb79d2c for woocommerce
commit 1544cb79d2c7b247c30335440873cf446d5d34bc
Author: Bill Wilson <bill@theconsultancy.co.uk>
Date: Wed Oct 14 16:56:41 2026 +0100
Issue with cart undo and Referrer-Policy: no-referrer (#68704)
* Issue with cart undo and Referrer-Policy: no-referrer
classic cart Undo fails with Referrer-Policy: no-referrer
WC_Cart_Session::clean_up_removed_cart_contents() on template_redirect already skips removed_item and undo_item requests, but not the original remove_item request.
After removing an item, WC_Form_Handler::update_cart_action() only redirects to ?removed_item=1 when wp_get_referer() returns a value. With Referrer-Policy: no-referrer, no referrer is available, so that redirect and its exit do not occur.
Execution continues to template_redirect during the original remove_item request. The new cleanup method then clears removed_cart_contents, deleting the data required by the Undo link.
Adding `remove_item` check fixes the missing case.
* Add changefile(s) from automation for the following project(s): woocommerce
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/68704-patch-1 b/plugins/woocommerce/changelog/68704-patch-1
new file mode 100644
index 00000000000..2657938b676
--- /dev/null
+++ b/plugins/woocommerce/changelog/68704-patch-1
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix classic cart Undo when the site uses Referrer-Policy: no-referrer.
diff --git a/plugins/woocommerce/includes/class-wc-cart-session.php b/plugins/woocommerce/includes/class-wc-cart-session.php
index a5f17d40650..0dc416f6c62 100644
--- a/plugins/woocommerce/includes/class-wc-cart-session.php
+++ b/plugins/woocommerce/includes/class-wc-cart-session.php
@@ -820,7 +820,7 @@ final class WC_Cart_Session {
// Don't cleanup if user just removed an item (undo link is being displayed).
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
- if ( isset( $_GET['removed_item'] ) ) {
+ if ( isset( $_GET['remove_item'] ) || isset( $_GET['removed_item'] ) ) {
return;
}
diff --git a/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts b/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts
index 96690e16d7e..96b8be1449c 100644
--- a/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/cart/cart.spec.ts
@@ -78,80 +78,102 @@ const test = baseTest.extend( {
/* endregion */
/* region tests */
-test(
- 'can undo product removal in classic cart',
- { tag: [ tags.PAYMENTS, tags.SERVICES, tags.HPOS ] },
- async ( { page, products, tax } ) => {
- const slug = CLASSIC_CART_PAGE.slug;
-
- await test.step( 'add product to cart', async () => {
- await addAProductToCart( page, products[ 0 ].id, 1 );
- await page.goto( slug );
- await checkCartContent(
- true,
- page,
- [ { data: products[ 0 ], qty: 1 } ],
- tax
- );
- } );
-
- await test.step( 'remove product and verify undo link appears', async () => {
- await page
- .getByLabel( /Remove .* from cart/ )
- .first()
- .click();
-
- // Verify the product was removed
- await checkCartContent( true, page, [], tax );
-
- // Verify the undo link appears
- await expect(
- page.getByRole( 'link', { name: 'Undo?' } )
- ).toBeVisible();
- } );
-
- await test.step( 'click undo to restore product', async () => {
- await page.getByRole( 'link', { name: 'Undo?' } ).click();
-
- // Verify the product is back in the cart
- await checkCartContent(
- true,
- page,
- [ { data: products[ 0 ], qty: 1 } ],
- tax
- );
- } );
-
- await test.step( 'remove product again after undo', async () => {
- await page
- .getByLabel( /Remove .* from cart/ )
- .first()
- .click();
-
- // Verify the product was removed again
- await checkCartContent( true, page, [], tax );
-
- // Verify undo link is visible
- await expect(
- page.getByRole( 'link', { name: 'Undo?' } )
- ).toBeVisible();
- } );
-
- await test.step( 'verify undo link disappears after navigation', async () => {
- // Navigate to shop page and return to cart
- await page.goto( 'shop' );
- await page.goto( slug );
-
- // Verify cart is still empty
- await checkCartContent( true, page, [], tax );
-
- // Verify undo link is no longer visible (cleanup occurred)
- await expect(
- page.getByRole( 'link', { name: 'Undo?' } )
- ).toBeHidden();
- } );
- }
-);
+for ( const referrerPolicy of [
+ 'strict-origin-when-cross-origin',
+ 'no-referrer',
+] ) {
+ test(
+ `can undo product removal in classic cart (${ referrerPolicy })`,
+ { tag: [ tags.PAYMENTS, tags.SERVICES, tags.HPOS ] },
+ async ( { page, products, tax } ) => {
+ const slug = CLASSIC_CART_PAGE.slug;
+
+ await page.addInitScript( ( policy ) => {
+ document.addEventListener( 'DOMContentLoaded', () => {
+ const meta = document.createElement( 'meta' );
+ meta.name = 'referrer';
+ meta.content = policy;
+ document.head.appendChild( meta );
+ } );
+ }, referrerPolicy );
+
+ await test.step( 'add product to cart', async () => {
+ await addAProductToCart( page, products[ 0 ].id, 1 );
+ await page.goto( slug );
+ await checkCartContent(
+ true,
+ page,
+ [ { data: products[ 0 ], qty: 1 } ],
+ tax
+ );
+ } );
+
+ await test.step( 'remove product and verify undo link appears', async () => {
+ const removalRequest = page.waitForRequest( ( request ) =>
+ new URL( request.url() ).searchParams.has( 'remove_item' )
+ );
+ await page
+ .getByLabel( /Remove .* from cart/ )
+ .first()
+ .click();
+
+ const request = await removalRequest;
+ expect( Boolean( request.headers().referer ) ).toBe(
+ referrerPolicy !== 'no-referrer'
+ );
+
+ // Verify the product was removed
+ await checkCartContent( true, page, [], tax );
+
+ // Verify the undo link appears
+ await expect(
+ page.getByRole( 'link', { name: 'Undo?' } )
+ ).toBeVisible();
+ } );
+
+ await test.step( 'click undo to restore product', async () => {
+ await page.getByRole( 'link', { name: 'Undo?' } ).click();
+
+ // Verify the product is back in the cart
+ await checkCartContent(
+ true,
+ page,
+ [ { data: products[ 0 ], qty: 1 } ],
+ tax
+ );
+ } );
+
+ await test.step( 'remove product again after undo', async () => {
+ await page
+ .getByLabel( /Remove .* from cart/ )
+ .first()
+ .click();
+
+ // Verify the product was removed again
+ await checkCartContent( true, page, [], tax );
+
+ // Verify undo link is visible
+ await expect(
+ page.getByRole( 'link', { name: 'Undo?' } )
+ ).toBeVisible();
+ } );
+
+ await test.step( 'verify undo link disappears after navigation', async () => {
+ // Navigate to shop page and return to cart
+ await page.goto( 'shop' );
+ await page.goto( slug );
+
+ // Verify cart is still empty
+ await checkCartContent( true, page, [], tax );
+
+ // Verify undo link is no longer visible (cleanup occurred)
+ await expect(
+ page.getByRole( 'link', { name: 'Undo?' } )
+ ).toBeHidden();
+ } );
+ }
+ );
+}
cartPages.forEach( ( { name, slug } ) => {
test(