diff --git a/.gitignore b/.gitignore
index 01314e1a67139..0f26ae8ce291a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,6 +110,8 @@ wp-tests-config.php
# Files for local environment config
/docker-compose.override.yml
+/tools/local-env/default.local.template
+/tools/local-env/php-config.local.ini
# Visual regression test diffs
tests/visual-regression/specs/__snapshots__
diff --git a/src/wp-admin/includes/class-wp-list-table.php b/src/wp-admin/includes/class-wp-list-table.php
index 6568b1563bef4..2aca7639bbf15 100644
--- a/src/wp-admin/includes/class-wp-list-table.php
+++ b/src/wp-admin/includes/class-wp-list-table.php
@@ -1045,7 +1045,7 @@ protected function pagination( $which ) {
$current = $this->get_pagenum();
$removable_query_args = wp_removable_query_args();
- $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
+ $current_url = wp_get_current_request_url();
$current_url = remove_query_arg( $removable_query_args, $current_url );
@@ -1399,7 +1399,7 @@ public function get_column_count() {
public function print_column_headers( $with_id = true ) {
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
- $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
+ $current_url = wp_get_current_request_url();
$current_url = remove_query_arg( 'paged', $current_url );
// When users click on a column header to sort by other columns.
diff --git a/src/wp-admin/includes/misc.php b/src/wp-admin/includes/misc.php
index 2454aad1c8d8b..b9c6aa431bfbf 100644
--- a/src/wp-admin/includes/misc.php
+++ b/src/wp-admin/includes/misc.php
@@ -1391,7 +1391,7 @@ function wp_admin_canonical_url() {
}
// Ensure we're using an absolute URL.
- $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
+ $current_url = wp_get_current_request_url();
$filtered_url = remove_query_arg( $removable_query_args, $current_url );
/**
diff --git a/src/wp-includes/admin-bar.php b/src/wp-includes/admin-bar.php
index 9fc3c2b46b348..5bd0f265ba919 100644
--- a/src/wp-includes/admin-bar.php
+++ b/src/wp-includes/admin-bar.php
@@ -517,7 +517,7 @@ function wp_admin_bar_customize_menu( $wp_admin_bar ) {
return;
}
- $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ $current_url = wp_get_current_request_url();
if ( is_customize_preview() && $wp_customize->changeset_uuid() ) {
$current_url = remove_query_arg( 'customize_changeset_uuid', $current_url );
}
diff --git a/src/wp-includes/blocks/loginout.php b/src/wp-includes/blocks/loginout.php
index f83d8be424ece..5e4e80cc3634d 100644
--- a/src/wp-includes/blocks/loginout.php
+++ b/src/wp-includes/blocks/loginout.php
@@ -16,12 +16,8 @@
*/
function render_block_core_loginout( $attributes ) {
- /*
- * Build the redirect URL. This current url fetching logic matches with the core.
- *
- * @see https://github.com/WordPress/wordpress-develop/blob/6bf62e58d21739938f3bb3f9e16ba702baf9c2cc/src/wp-includes/general-template.php#L528.
- */
- $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+ // Build the redirect URL.
+ $current_url = wp_get_current_request_url();
$user_logged_in = is_user_logged_in();
diff --git a/src/wp-includes/canonical.php b/src/wp-includes/canonical.php
index 9315ba7fb7ff9..6badb2e561136 100644
--- a/src/wp-includes/canonical.php
+++ b/src/wp-includes/canonical.php
@@ -65,11 +65,9 @@ function redirect_canonical( $requested_url = null, $do_redirect = true ) {
return;
}
- if ( ! $requested_url && isset( $_SERVER['HTTP_HOST'] ) ) {
+ if ( ! $requested_url && isset( $_SERVER['REQUEST_URI'] ) ) {
// Build the URL in the address bar.
- $requested_url = is_ssl() ? 'https://' : 'http://';
- $requested_url .= $_SERVER['HTTP_HOST'];
- $requested_url .= $_SERVER['REQUEST_URI'];
+ $requested_url = wp_get_current_request_url();
}
$original = parse_url( $requested_url );
diff --git a/src/wp-includes/class-wp-recovery-mode.php b/src/wp-includes/class-wp-recovery-mode.php
index 7d1af1164185e..7523b0d5d1276 100644
--- a/src/wp-includes/class-wp-recovery-mode.php
+++ b/src/wp-includes/class-wp-recovery-mode.php
@@ -462,9 +462,7 @@ protected function redirect_protected() {
require_once ABSPATH . WPINC . '/pluggable.php';
}
- $scheme = is_ssl() ? 'https://' : 'http://';
-
- $url = "{$scheme}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
+ $url = wp_get_current_request_url();
wp_safe_redirect( $url );
exit;
}
diff --git a/src/wp-includes/feed.php b/src/wp-includes/feed.php
index 821a3eb9be804..487bebf7ea151 100644
--- a/src/wp-includes/feed.php
+++ b/src/wp-includes/feed.php
@@ -668,14 +668,7 @@ function rss2_site_icon() {
* @return string Correct link for the atom:self element.
*/
function get_self_link() {
- $parsed = parse_url( home_url() );
-
- $domain = $parsed['host'];
- if ( isset( $parsed['port'] ) ) {
- $domain .= ':' . $parsed['port'];
- }
-
- return set_url_scheme( 'http://' . $domain . wp_unslash( $_SERVER['REQUEST_URI'] ) );
+ return wp_get_current_request_url();
}
/**
diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php
index bb19b8e5f9eea..985e5d940a926 100644
--- a/src/wp-includes/functions.php
+++ b/src/wp-includes/functions.php
@@ -1923,7 +1923,7 @@ function wp_nonce_field( $action = -1, $name = '_wpnonce', $referer = true, $dis
* @return string Referer field HTML markup.
*/
function wp_referer_field( $display = true ) {
- $request_url = remove_query_arg( '_wp_http_referer' );
+ $request_url = remove_query_arg( '_wp_http_referer', wp_get_current_request_url() );
$referer_field = '';
if ( $display ) {
@@ -7464,7 +7464,7 @@ function wp_auth_check_load() {
*/
function wp_auth_check_html() {
$login_url = wp_login_url();
- $current_domain = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'];
+ $current_domain = network_home_url();
$same_domain = str_starts_with( $login_url, $current_domain );
/**
diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php
index 640bc54c8e754..c4c7c00c02ad7 100644
--- a/src/wp-includes/general-template.php
+++ b/src/wp-includes/general-template.php
@@ -525,7 +525,7 @@ function wp_login_form( $args = array() ) {
$defaults = array(
'echo' => true,
// Default 'redirect' value takes the user back to the request URI.
- 'redirect' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
+ 'redirect' => wp_get_current_request_url(),
'form_id' => 'loginform',
'label_username' => __( 'Username or Email Address' ),
'label_password' => __( 'Password' ),
diff --git a/src/wp-includes/link-template.php b/src/wp-includes/link-template.php
index d6f97a845f65e..5f024e4f6384f 100644
--- a/src/wp-includes/link-template.php
+++ b/src/wp-includes/link-template.php
@@ -3817,6 +3817,56 @@ function network_home_url( $path = '', $scheme = null ) {
return apply_filters( 'network_home_url', $url, $path, $orig_scheme );
}
+/**
+ * Builds a full URL for the current request using the site's configured host.
+ *
+ * Replaces raw `$_SERVER['HTTP_HOST']` with the host from `home_url()`, which
+ * respects the DB-configured site address. This is important behind reverse
+ * proxies or load balancers where `HTTP_HOST` may not match the public host.
+ *
+ * If the request URI already contains the home path (standard setup), only the
+ * scheme and host are swapped. If the home path is missing from the request URI
+ * (e.g. a reverse proxy stripped a path prefix), the home path is prepended
+ * automatically.
+ *
+ * @since 6.9.0
+ *
+ * @see https://core.trac.wordpress.org/ticket/53998
+ *
+ * @param string|null $request_uri Optional. The request URI to use. Defaults to
+ * `$_SERVER['REQUEST_URI']`.
+ * @return string Full URL of the current request.
+ */
+function wp_get_current_request_url( $request_uri = null ) {
+ if ( null === $request_uri ) {
+ $request_uri = $_SERVER['REQUEST_URI'];
+ }
+
+ $home = home_url( '/' );
+ $home_path = wp_parse_url( $home, PHP_URL_PATH );
+
+ if ( ! $home_path ) {
+ $home_path = '/';
+ }
+
+ if ( str_starts_with( $request_uri, $home_path ) ) {
+ // Standard case: REQUEST_URI already includes the home path.
+ // Just replace scheme + host, keep the request URI as-is.
+ $parsed = wp_parse_url( $home );
+ $host = isset( $parsed['host'] ) ? $parsed['host'] : $_SERVER['HTTP_HOST'];
+ if ( isset( $parsed['port'] ) ) {
+ $host .= ':' . $parsed['port'];
+ }
+ $url = set_url_scheme( 'http://' . $host . $request_uri );
+ } else {
+ // Reverse-proxy case: REQUEST_URI is missing the home path prefix.
+ // Let home_url() prepend it.
+ $url = home_url( $request_uri );
+ }
+
+ return $url;
+}
+
/**
* Retrieves the URL to the admin area for the network.
*
diff --git a/src/wp-includes/nav-menu-template.php b/src/wp-includes/nav-menu-template.php
index d90fdfa8061ab..0e8a8de0e5d76 100644
--- a/src/wp-includes/nav-menu-template.php
+++ b/src/wp-includes/nav-menu-template.php
@@ -474,7 +474,7 @@ function _wp_menu_item_classes_by_context( &$menu_items ) {
$active_parent_item_ids[] = (int) $menu_item->menu_item_parent;
// If the menu item corresponds to the currently requested URL.
- } elseif ( 'custom' === $menu_item->object && isset( $_SERVER['HTTP_HOST'] ) ) {
+ } elseif ( 'custom' === $menu_item->object && isset( $_SERVER['REQUEST_URI'] ) ) {
$_root_relative_current = untrailingslashit( $_SERVER['REQUEST_URI'] );
// If it's the customize page then it will strip the query var off the URL before entering the comparison block.
@@ -482,7 +482,7 @@ function _wp_menu_item_classes_by_context( &$menu_items ) {
$_root_relative_current = strtok( untrailingslashit( $_SERVER['REQUEST_URI'] ), '?' );
}
- $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_root_relative_current );
+ $current_url = wp_get_current_request_url( $_root_relative_current );
$raw_item_url = strpos( $menu_item->url, '#' ) ? substr( $menu_item->url, 0, strpos( $menu_item->url, '#' ) ) : $menu_item->url;
$item_url = set_url_scheme( untrailingslashit( $raw_item_url ) );
$_indexless_current = untrailingslashit( preg_replace( '/' . preg_quote( $wp_rewrite->index, '/' ) . '$/', '', $current_url ) );
diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php
index 688f910dd7390..5597663f6c248 100644
--- a/src/wp-includes/pluggable.php
+++ b/src/wp-includes/pluggable.php
@@ -1287,13 +1287,8 @@ function auth_redirect() {
// If https is required and request is http, redirect.
if ( $secure && ! is_ssl() && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
- if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
- wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
- exit;
- } else {
- wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
- exit;
- }
+ wp_safe_redirect( set_url_scheme( wp_get_current_request_url(), 'https' ) );
+ exit;
}
/**
@@ -1318,13 +1313,8 @@ function auth_redirect() {
// If the user wants ssl but the session is not ssl, redirect.
if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
- if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
- wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
- exit;
- } else {
- wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
- exit;
- }
+ wp_safe_redirect( set_url_scheme( wp_get_current_request_url(), 'https' ) );
+ exit;
}
return; // The cookie is good, so we're done.
@@ -1332,16 +1322,15 @@ function auth_redirect() {
// The cookie is no good, so force login.
nocache_headers();
-
if ( str_contains( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) {
$redirect = wp_get_referer();
} else {
- $redirect = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
+ $redirect = wp_get_current_request_url();
}
$login_url = wp_login_url( $redirect, true );
- wp_redirect( $login_url );
+ wp_safe_redirect( $login_url );
exit;
}
endif;
diff --git a/src/wp-login.php b/src/wp-login.php
index 60d9c21f3ddf1..3aa3d8f3f62f0 100644
--- a/src/wp-login.php
+++ b/src/wp-login.php
@@ -13,13 +13,8 @@
// Redirect to HTTPS login if forced to use SSL.
if ( force_ssl_admin() && ! is_ssl() ) {
- if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
- wp_safe_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
- exit;
- } else {
- wp_safe_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
- exit;
- }
+ wp_safe_redirect( set_url_scheme( wp_get_current_request_url(), 'https' ) );
+ exit;
}
/**
diff --git a/tests/phpunit/tests/feed/getSelfLink.php b/tests/phpunit/tests/feed/getSelfLink.php
new file mode 100644
index 0000000000000..e7f8f0eec84cc
--- /dev/null
+++ b/tests/phpunit/tests/feed/getSelfLink.php
@@ -0,0 +1,127 @@
+original_request_uri = $_SERVER['REQUEST_URI'];
+ }
+
+ public function tear_down() {
+ $_SERVER['REQUEST_URI'] = $this->original_request_uri;
+ unset( $_SERVER['HTTPS'] );
+
+ parent::tear_down();
+ }
+
+ /**
+ * Tests that get_self_link() returns a full URL using the home host.
+ *
+ * @ticket 53998
+ */
+ public function test_returns_full_url() {
+ $_SERVER['REQUEST_URI'] = '/feed/';
+
+ $url = get_self_link();
+
+ $this->assertStringStartsWith( 'http', $url );
+ $this->assertStringContainsString( '/feed/', $url );
+ }
+
+ /**
+ * Tests that get_self_link() uses the host from home_url().
+ *
+ * @ticket 53998
+ */
+ public function test_host_from_home_url() {
+ $original_home = get_option( 'home' );
+ update_option( 'home', 'http://feeds.example.com' );
+
+ $_SERVER['REQUEST_URI'] = '/feed/';
+
+ $url = get_self_link();
+
+ update_option( 'home', $original_home );
+
+ $this->assertStringContainsString( 'feeds.example.com', $url );
+ }
+
+ /**
+ * Tests that get_self_link() reflects the current request scheme.
+ *
+ * @ticket 53998
+ */
+ public function test_scheme_follows_is_ssl() {
+ $_SERVER['REQUEST_URI'] = '/feed/';
+ $_SERVER['HTTPS'] = 'on';
+
+ $url = get_self_link();
+
+ $this->assertStringStartsWith( 'https://', $url );
+ }
+
+ /**
+ * Tests that get_self_link() preserves query strings.
+ *
+ * @ticket 53998
+ */
+ public function test_preserves_query_string() {
+ $_SERVER['REQUEST_URI'] = '/feed/?cat=1&paged=2';
+
+ $url = get_self_link();
+
+ $this->assertStringContainsString( 'cat=1', $url );
+ $this->assertStringContainsString( 'paged=2', $url );
+ }
+
+ /**
+ * Tests that get_self_link() includes the subdirectory prefix.
+ *
+ * @ticket 53998
+ */
+ public function test_includes_subdirectory_prefix() {
+ $original_home = get_option( 'home' );
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/blog' );
+
+ $_SERVER['REQUEST_URI'] = '/blog/feed/';
+
+ $url = get_self_link();
+
+ update_option( 'home', $original_home );
+
+ $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/blog/feed/', $url );
+ }
+
+ /**
+ * Tests the reverse-proxy case where the path prefix is stripped.
+ *
+ * @ticket 53998
+ */
+ public function test_reverse_proxy_prepends_home_path() {
+ $original_home = get_option( 'home' );
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/subsite' );
+
+ $_SERVER['REQUEST_URI'] = '/feed/';
+
+ $url = get_self_link();
+
+ update_option( 'home', $original_home );
+
+ $this->assertSame( 'http://' . WP_TESTS_DOMAIN . '/subsite/feed/', $url );
+ }
+}
diff --git a/tests/phpunit/tests/functions/wpRefererField.php b/tests/phpunit/tests/functions/wpRefererField.php
index 72e2fe99d5fde..f8d77e4addb66 100644
--- a/tests/phpunit/tests/functions/wpRefererField.php
+++ b/tests/phpunit/tests/functions/wpRefererField.php
@@ -11,23 +11,49 @@
*/
class Tests_Functions_wpRefererField extends WP_UnitTestCase {
+ /**
+ * The original value of `$_SERVER['REQUEST_URI']`.
+ *
+ * @var string
+ */
+ private $original_request_uri;
+
+ public function set_up() {
+ parent::set_up();
+
+ $this->original_request_uri = $_SERVER['REQUEST_URI'];
+ }
+
+ public function tear_down() {
+ $_SERVER['REQUEST_URI'] = $this->original_request_uri;
+
+ parent::tear_down();
+ }
+
/**
* @ticket 55578
+ * @ticket 53998
*/
public function test_wp_referer_field() {
$_SERVER['REQUEST_URI'] = '/test/';
wp_referer_field();
- $this->expectOutputString( '' );
+ $this->expectOutputString(
+ ''
+ );
}
/**
* @ticket 55578
+ * @ticket 53998
*/
public function test_wp_referer_field_return() {
$_SERVER['REQUEST_URI'] = '/test/';
- $this->assertSame( '', wp_referer_field( false ) );
+ $this->assertSame(
+ '',
+ wp_referer_field( false )
+ );
}
/**
@@ -42,7 +68,8 @@ public function test_wp_referer_field_return() {
public function test_wp_referer_field_should_respect_display_arg( $display ) {
$actual = $display ? get_echo( 'wp_referer_field' ) : wp_referer_field( false );
- $this->assertSame( '', $actual );
+ $this->assertStringContainsString( '_wp_http_referer', $actual );
+ $this->assertStringContainsString( 'get_referer_value( $actual );
+
+ $this->assertStringContainsString( 'edit.php', $value );
+ $this->assertStringNotContainsString( '_wp_http_referer', $value );
+ }
- $_SERVER['REQUEST_URI'] = $old_request_uri;
+ /**
+ * Tests that the referer value is an absolute URL.
+ *
+ * @ticket 53998
+ */
+ public function test_wp_referer_field_value_is_absolute_url() {
+ $_SERVER['REQUEST_URI'] = '/my-account/';
- $this->assertSame( '', $actual );
+ $actual = wp_referer_field( false );
+ $value = $this->get_referer_value( $actual );
+
+ $this->assertStringStartsWith( 'http', $value, 'The _wp_http_referer value should be an absolute URL.' );
+ }
+
+ /**
+ * Tests that the referer value includes the home path for subdirectory installs.
+ *
+ * @ticket 53998
+ */
+ public function test_wp_referer_field_includes_home_path() {
+ $original_home = get_option( 'home' );
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/subdir' );
+
+ $_SERVER['REQUEST_URI'] = '/subdir/my-account/';
+
+ $actual = wp_referer_field( false );
+ $value = $this->get_referer_value( $actual );
+
+ update_option( 'home', $original_home );
+
+ $this->assertStringContainsString( '/subdir/my-account/', $value );
+ }
+
+ /**
+ * Tests that the _wp_http_referer query arg is stripped from the value.
+ *
+ * @ticket 54106
+ * @ticket 53998
+ */
+ public function test_wp_referer_field_strips_existing_referer_arg() {
+ $_SERVER['REQUEST_URI'] = '/wp-admin/edit.php?_wp_http_referer=%2Fwp-admin%2Fedit.php&post_type=page';
+
+ $actual = wp_referer_field( false );
+ $value = $this->get_referer_value( $actual );
+
+ $this->assertStringNotContainsString( '_wp_http_referer', $value );
+ $this->assertStringContainsString( 'post_type=page', $value );
+ }
+
+ /**
+ * Extracts the value attribute from a wp_referer_field() output string.
+ *
+ * @param string $html The HTML output from wp_referer_field().
+ * @return string The value of the value attribute.
+ */
+ private function get_referer_value( $html ) {
+ preg_match( '/value="([^"]*)"/', $html, $matches );
+ return html_entity_decode( $matches[1] ?? '' );
}
}
diff --git a/tests/phpunit/tests/link/wpGetCurrentRequestUrl.php b/tests/phpunit/tests/link/wpGetCurrentRequestUrl.php
new file mode 100644
index 0000000000000..df0c8fa8a4aa3
--- /dev/null
+++ b/tests/phpunit/tests/link/wpGetCurrentRequestUrl.php
@@ -0,0 +1,321 @@
+original_request_uri = $_SERVER['REQUEST_URI'];
+ $this->original_http_host = $_SERVER['HTTP_HOST'];
+ $this->original_home = get_option( 'home' );
+ }
+
+ public function tear_down() {
+ $_SERVER['REQUEST_URI'] = $this->original_request_uri;
+ $_SERVER['HTTP_HOST'] = $this->original_http_host;
+ update_option( 'home', $this->original_home );
+ unset( $_SERVER['HTTPS'] );
+
+ parent::tear_down();
+ }
+
+ /**
+ * Tests that the function returns a full URL for a root install.
+ *
+ * @ticket 53998
+ */
+ public function test_returns_full_url_for_root_install() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '/sample-page/?foo=bar';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/sample-page/?foo=bar',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests that the function includes the home path for a subdirectory install
+ * when the REQUEST_URI already contains the subdirectory.
+ *
+ * @ticket 53998
+ */
+ public function test_standard_subdirectory_install() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/subdir' );
+ $_SERVER['REQUEST_URI'] = '/subdir/sample-page/?foo=bar';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/subdir/sample-page/?foo=bar',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests the reverse-proxy case where the proxy strips the path prefix
+ * from REQUEST_URI before forwarding to WordPress.
+ *
+ * @ticket 53998
+ */
+ public function test_reverse_proxy_stripped_prefix() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/branch-slug' );
+ $_SERVER['REQUEST_URI'] = '/sample-page/?foo=bar';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/branch-slug/sample-page/?foo=bar',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests the reverse-proxy case with a root-level request.
+ *
+ * @ticket 53998
+ */
+ public function test_reverse_proxy_root_request() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/branch-slug' );
+ $_SERVER['REQUEST_URI'] = '/';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/branch-slug/',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests that an explicit $request_uri parameter overrides $_SERVER.
+ *
+ * @ticket 53998
+ */
+ public function test_explicit_request_uri_parameter() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '/should-not-be-used/';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/explicit-path/',
+ wp_get_current_request_url( '/explicit-path/' )
+ );
+ }
+
+ /**
+ * Tests that the scheme follows is_ssl() for a standard install.
+ *
+ * @ticket 53998
+ */
+ public function test_scheme_follows_is_ssl_https() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '/test/';
+ $_SERVER['HTTPS'] = 'on';
+
+ $this->assertStringStartsWith( 'https://', wp_get_current_request_url() );
+ }
+
+ /**
+ * Tests that the scheme is HTTP when HTTPS is off.
+ *
+ * @ticket 53998
+ */
+ public function test_scheme_follows_is_ssl_http() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '/test/';
+ $_SERVER['HTTPS'] = 'off';
+
+ $this->assertStringStartsWith( 'http://', wp_get_current_request_url() );
+ }
+
+ /**
+ * Tests that the host comes from home_url(), not from HTTP_HOST.
+ *
+ * @ticket 53998
+ */
+ public function test_host_from_home_url_not_http_host() {
+ update_option( 'home', 'http://configured-host.example.com' );
+ $_SERVER['HTTP_HOST'] = 'container-host.internal';
+ $_SERVER['REQUEST_URI'] = '/page/';
+
+ $url = wp_get_current_request_url();
+
+ $this->assertStringContainsString( 'configured-host.example.com', $url );
+ $this->assertStringNotContainsString( 'container-host.internal', $url );
+ }
+
+ /**
+ * Tests that a port in the home URL is preserved.
+ *
+ * @ticket 53998
+ */
+ public function test_port_in_home_url_is_preserved() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . ':8080' );
+ $_SERVER['REQUEST_URI'] = '/page/';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . ':8080/page/',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests that query strings are preserved in the returned URL.
+ *
+ * @ticket 53998
+ */
+ public function test_query_string_preserved() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '/wp-admin/edit.php?post_type=page&orderby=date';
+
+ $url = wp_get_current_request_url();
+
+ $this->assertStringContainsString( 'post_type=page', $url );
+ $this->assertStringContainsString( 'orderby=date', $url );
+ }
+
+ /**
+ * Tests the reverse-proxy case with query strings.
+ *
+ * @ticket 53998
+ */
+ public function test_reverse_proxy_with_query_string() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/branch' );
+ $_SERVER['REQUEST_URI'] = '/wp-admin/edit.php?post_type=page';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/branch/wp-admin/edit.php?post_type=page',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests with an empty REQUEST_URI.
+ *
+ * @ticket 53998
+ */
+ public function test_empty_request_uri() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '';
+
+ $url = wp_get_current_request_url();
+
+ $this->assertSame( 'http://' . WP_TESTS_DOMAIN, $url );
+ }
+
+ /**
+ * Tests that a trailing-slash home path is handled correctly.
+ *
+ * The home_url('/') always produces a trailing slash. When REQUEST_URI
+ * starts with it (which is always the case for '/'), the standard path
+ * should be taken.
+ *
+ * @ticket 53998
+ */
+ public function test_root_home_with_trailing_slash() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN );
+ $_SERVER['REQUEST_URI'] = '/wp-login.php';
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/wp-login.php',
+ wp_get_current_request_url()
+ );
+ }
+
+ /**
+ * Tests the reverse-proxy case with the explicit $request_uri parameter.
+ *
+ * @ticket 53998
+ */
+ public function test_reverse_proxy_with_explicit_request_uri() {
+ update_option( 'home', 'http://' . WP_TESTS_DOMAIN . '/prefix' );
+
+ $this->assertSame(
+ 'http://' . WP_TESTS_DOMAIN . '/prefix/my-account/',
+ wp_get_current_request_url( '/my-account/' )
+ );
+ }
+
+ /**
+ * Tests data provider scenarios for standard and reverse-proxy installs.
+ *
+ * @ticket 53998
+ *
+ * @dataProvider data_request_url_scenarios
+ *
+ * @param string $home The home option value.
+ * @param string $request_uri The value for `$_SERVER['REQUEST_URI']`.
+ * @param string $expected The expected URL.
+ */
+ public function test_request_url_scenarios( $home, $request_uri, $expected ) {
+ update_option( 'home', $home );
+ $_SERVER['REQUEST_URI'] = $request_uri;
+
+ $this->assertSame( $expected, wp_get_current_request_url() );
+ }
+
+ /**
+ * Data provider.
+ *
+ * @return array[]
+ */
+ public function data_request_url_scenarios() {
+ $domain = WP_TESTS_DOMAIN;
+
+ return array(
+ 'root install, simple path' => array(
+ 'home' => "http://{$domain}",
+ 'request_uri' => '/hello-world/',
+ 'expected' => "http://{$domain}/hello-world/",
+ ),
+ 'root install, wp-admin path with query' => array(
+ 'home' => "http://{$domain}",
+ 'request_uri' => '/wp-admin/options.php?updated=1',
+ 'expected' => "http://{$domain}/wp-admin/options.php?updated=1",
+ ),
+ 'subdirectory install, matching REQUEST_URI' => array(
+ 'home' => "http://{$domain}/wp",
+ 'request_uri' => '/wp/hello-world/',
+ 'expected' => "http://{$domain}/wp/hello-world/",
+ ),
+ 'reverse proxy, stripped /wp prefix' => array(
+ 'home' => "http://{$domain}/wp",
+ 'request_uri' => '/hello-world/',
+ 'expected' => "http://{$domain}/wp/hello-world/",
+ ),
+ 'reverse proxy, stripped /branch prefix' => array(
+ 'home' => "http://{$domain}/deploy-branch",
+ 'request_uri' => '/my-account/',
+ 'expected' => "http://{$domain}/deploy-branch/my-account/",
+ ),
+ 'reverse proxy, root request' => array(
+ 'home' => "http://{$domain}/deploy-branch",
+ 'request_uri' => '/',
+ 'expected' => "http://{$domain}/deploy-branch/",
+ ),
+ );
+ }
+}