[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