@vidja is very close and definitely on the right track with redirect_canonical
.
I dug into this issue a bit further myself to figure out that this is a combination of issues between both PHP itself and this redirect logic in WordPress. If you have a look at the php global $_GET
directly, you'll find that is where the query string params are being parsed "incorrectly". For instance:
// For a url like: /some/path/?tracking.id=123
var_dump($_GET);
// array(1) { ["tracking_id"]=> string(3) "123" }
If you continue to dig through the redirect_canonical
code you'll eventually find the culprit to be a stack of function calls, ending with php's parse_str
method:
// https://developer.wordpress.org/reference/functions/_remove_qs_args_if_not_in_url/
_remove_qs_args_if_not_in_url()
// https://developer.wordpress.org/reference/functions/remove_query_arg/
remove_query_arg()
// https://developer.wordpress.org/reference/functions/add_query_arg/
add_query_arg()
// https://developer.wordpress.org/reference/functions/wp_parse_str/
wp_parse_str()
// https://www.php.net/manual/en/function.parse-str.php
parse_str()
Testing out parse_str
directly gives you the same result as looking at $_GET:
$arr = [];
parse_str('tracking.id=123', $arr);
var_dump( $arr );
// array(1) { ["tracking_id"]=> string(3) "123" }
All this to say that there is a slightly better way to setup your own filter so that you get a more robust result. For instance, the way that @vidja wrote his filter would result in pages not being properly redirected when you'd want them to be (and as a result, WordPress showing a 404 not found page). Say you had a page with a slug of /sample-page
and you tried visiting your site through a url like: /sampl?tracking.id=123
. Wordpress would want to redirect that to /sample-page/?tracking_id=123
, but @vidja's code would instead return the original (bad) url since tracking_id
is set in $_GET. So, a better way to handle this, in my opinion, would be to replace the specific query string param you care about inside the redirect url, that way Wordpress can do it's thing to redirect pages as it see's fit, but you can also maintain your tracking.id
properly. Here is what that would look like:
add_filter( 'redirect_canonical', function ( $redirect_url, $requested_url ) {
return preg_replace( '/tracking_id=/', 'tracking.id=', $redirect_url );
}, 10, 2 );
You could even do something like the following if you had multiple query string parameters with period's that you needed to maintain:
add_filter( 'redirect_canonical', function ( $redirect_url, $requested_url ) {
$query_params = [
'tracking_id' => 'tracking.id',
'foo_bar' => 'foo.bar',
];
foreach ( $query_params as $search => $replace ) {
$redirect_url = preg_replace( '/'.$search.'=/', $replace.'=', $redirect_url );
}
return $redirect_url;
}, 10, 2 );