[wp-trac] [WordPress Trac] #64376: redirect_canonical() causes unnecessary 301 redirects for query string encoding variants (+ vs %20)

WordPress Trac noreply at wordpress.org
Mon Dec 8 11:00:27 UTC 2025


#64376: redirect_canonical() causes unnecessary 301 redirects for query string
encoding variants (+ vs %20)
----------------------------+-----------------------------
 Reporter:  robbertvancaem  |      Owner:  (none)
     Type:  defect (bug)    |     Status:  new
 Priority:  normal          |  Milestone:  Awaiting Review
Component:  Canonical       |    Version:  6.8.3
 Severity:  normal          |   Keywords:
  Focuses:                  |
----------------------------+-----------------------------
 == Summary

   `redirect_canonical()` issues 301 redirects when query strings contain
 `+` characters, redirecting them to `%20` encoded equivalents. Since both
 are functionally identical in query strings (RFC 3986), these redirects
 serve no purpose and
   cause real-world problems.

   == Environment

   * '''WordPress version:''' 6.8.3
   * '''PHP version:''' 8.3
   * '''Hosting:''' Kinsta (with page caching)
   * '''Relevant setting:''' Static front page enabled

   == Steps to Reproduce

   1. Set up a WordPress site with a static front page
   2. Visit: `https://example.com/?utm_content=Hello+World`
   3. WordPress redirects 301 to:
 `https://example.com/?utm_content=Hello%20World`

   == Root Cause

   In `wp-includes/canonical.php` (lines 576-594), query strings are
 rebuilt using `rawurlencode_deep()`, which converts `+` to `%20`. The
 function then compares the rebuilt URL to the original and issues a
 redirect, even though both URLs
   resolve to identical content.

   == Real-World Impact

   This caused a severe production incident on our site:

   * Marketing campaign URLs with `+` in UTM parameters triggered 301
 redirects
   * Our page cache stored the 301 response
   * All subsequent visitors received cached 301 redirects, creating
 redirect loops
   * Campaign success rate dropped from 100% to 2% over several days
   * Affected thousands of users before root cause was identified

   This affects any site using:
   * Page caching (Kinsta, WP Super Cache, W3 Total Cache, etc.)
   * Marketing campaigns with spaces in UTM parameters
   * Any query strings containing `+` characters

   == Proposed Fix

   Before returning a redirect, compare decoded URLs:

   {{{#!php
   // In redirect_canonical(), before the final redirect:
   if ( $redirect_url && urldecode( $redirect_url ) === urldecode(
 $requested_url ) ) {
       return false;
   }
   }}}

   This preserves all meaningful redirects (www normalization, trailing
 slashes, pretty permalinks) while preventing encoding-only redirects that
 provide no SEO or functional benefit.

   == Current Workaround

   {{{#!php
   add_filter( 'redirect_canonical', function( $redirect_url,
 $requested_url ) {
       if ( $redirect_url && urldecode( $redirect_url ) === urldecode(
 $requested_url ) ) {
           return false;
       }
       return $redirect_url;
   }, 10, 2 );
   }}}

   == Why This Should Be Fixed

   1. '''No benefit:''' Both `+` and `%20` represent spaces in query
 strings; redirecting between them serves no SEO or functional purpose
   2. '''Breaks caching:''' 301 responses get cached, causing redirect
 loops
   3. '''Breaks analytics:''' Redirect strips original referrer data
   4. '''Violates user expectations:''' UTM parameters are designed for
 variation; users don't expect redirects based on encoding

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/64376>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list