Skip to content

Fix redirect comparison to account for port difference #8977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from
25 changes: 20 additions & 5 deletions src/wp-includes/canonical.php
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,10 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) {
// www.example.com vs. example.com
$user_home = parse_url( home_url() );

if ( ! empty( $user_home['scheme'] ) ) {
$redirect['scheme'] = $user_home['scheme'];
}

if ( ! empty( $user_home['host'] ) ) {
$redirect['host'] = $user_home['host'];
}
Expand Down Expand Up @@ -717,13 +721,24 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) {
$original_host_low = strtolower( $original['host'] );
$redirect_host_low = strtolower( $redirect['host'] );

$original_port = isset( $original['port'] ) ? strtolower( $original['port'] ) : '80';
$redirect_port = isset( $redirect['port'] ) ? strtolower( $redirect['port'] ) : '80';

/*
* Ignore differences in host capitalization, as this can lead to infinite redirects.
* Only redirect no-www <=> yes-www.
* Preserve original host casing if the hostnames are identical (ignoring case),
* or differ in a way that is not just a www <=> non-www variation,
* and the port is the same.
*
* This prevents unnecessary redirects and avoids redirect loops due to host casing,
* but still allows canonical redirects between www and non-www variants.
*/
if ( $original_host_low === $redirect_host_low
|| ( 'www.' . $original_host_low !== $redirect_host_low
&& 'www.' . $redirect_host_low !== $original_host_low )
if (
$original_host_low === $redirect_host_low
|| (
'www.' . $original_host_low !== $redirect_host_low
&& 'www.' . $redirect_host_low !== $original_host_low
&& $original_port === $redirect_port
)
) {
$redirect['host'] = $original['host'];
}
Expand Down
88 changes: 88 additions & 0 deletions tests/phpunit/tests/canonical.php
Original file line number Diff line number Diff line change
Expand Up @@ -545,4 +545,92 @@ public function data_canonical_attachment_page_redirect_with_option_disabled() {
),
);
}

/**
* Test domain and port redirections.
*
* @ticket 33821
* @dataProvider data_domain_and_port_redirections
*
* @covers ::redirect_canonical
*
* @param string $home_url The home URL to set.
* @param string $site_url The site URL to set.
* @param string $request_url The URL to test redirection for.
* @param string $expected_url The expected redirect URL, or null if no redirect.
* @param string $failure_msg The message to display on test failure.
*/
public function test_domain_and_port_redirections( $home_url, $site_url, $request_url, $expected_url, $failure_msg ) {
update_option( 'home', $home_url );
update_option( 'siteurl', $site_url );

$redirect = redirect_canonical( $request_url, false );

$this->assertSame( $expected_url, $redirect, $failure_msg );
}

/**
* Data provider for test_domain_and_port_redirections().
*
* @return array[] {
* @type string $home_url The home URL to set.
* @type string $site_url The site URL to set.
* @type string $request_url The URL to test redirection for.
* @type string $expected_url The expected redirect URL, or null if no redirect.
* @type string $failure_msg The message to display on test failure.
* }
*/
public function data_domain_and_port_redirections() {
return array(
'non-standard localhost port to canonical domain' => array(
'home_url' => 'http://example.com',
'site_url' => 'http://example.com',
'request_url' => 'http://localhost:10020/',
'expected_url' => 'http://example.com/',
'failure_msg' => 'Failed to redirect non-standard localhost port to canonical domain',
),
'non-standard localhost port to canonical domain with SSL' => array(
'home_url' => 'https://example.com',
'site_url' => 'https://example.com',
'request_url' => 'http://localhost:10020/',
'expected_url' => 'https://example.com/',
'failure_msg' => 'Failed to redirect non-standard localhost port to canonical domain with SSL',
),
'different host casing do not redirect' => array(
'home_url' => 'http://example.com',
'site_url' => 'http://example.com',
'request_url' => 'http://Example.com/',
'expected_url' => null,
'failure_msg' => 'Should not redirect when only host casing differs',
),
'different host casing with port redirect without host change' => array(
'home_url' => 'http://example.com:8080',
'site_url' => 'http://example.com:8080',
'request_url' => 'http://Example.com:10200/',
'expected_url' => 'http://Example.com:8080/',
'failure_msg' => 'Failed to redirect to correct port while preserving host casing',
),
'www to non-www' => array(
'home_url' => 'http://example.com',
'site_url' => 'http://example.com',
'request_url' => 'http://www.example.com/',
'expected_url' => 'http://example.com/',
'failure_msg' => 'Failed to redirect www to non-www domain',
),
'non-www to www' => array(
'home_url' => 'http://www.example.com',
'site_url' => 'http://www.example.com',
'request_url' => 'http://example.com/',
'expected_url' => 'http://www.example.com/',
'failure_msg' => 'Failed to redirect non-www to www domain',
),
'port for same host' => array(
'home_url' => 'http://example.com:8080',
'site_url' => 'http://example.com:8080',
'request_url' => 'http://example.com:10200/',
'expected_url' => 'http://example.com:8080/',
'failure_msg' => 'Failed to redirect to correct port for same host',
),
);
}
}
Loading