Opened 11 years ago
Last modified 8 years ago
#34634 new defect (bug)
Empty PHP_SELF causes 404 pages to load front page with 200 status code
| Reported by: | l3rady | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Bootstrap/Load | Version: | 2.0 |
| Severity: | normal | Keywords: | has-patch has-unit-tests |
| Cc: | Focuses: | multisite |
Description
I've been having this issue where visiting a non-existent page in wordpress on our live server would result in the homepage being loaded with a 200 OK response, however on local and staging going to an non-existent page would result in the correct 404 page and template being loaded.
I have a standard 4.3.1 WP install subdomain base multisite with pretty permalinks enabled running with Nginx/PHP-FPM.
I've looked to see what was different between local and live and it turns out on live $_SERVER['PHP_SELF'] is being set blank and on staging it is being set to /index.php. So to test I set at the top of my index.php file the following line $_SERVER['PHP_SELF'] = '';. Then when running on staging I was now getting the homepage load on a non-existent page.
So I've fired up my debugger to see what is going on in core and here are my findings.
In wp-load.php we set PHP_SELF to something based off REQUEST_URI
// Fix empty PHP_SELF $PHP_SELF = $_SERVER['PHP_SELF']; if ( empty( $PHP_SELF ) ) $_SERVER['PHP_SELF'] = $PHP_SELF = preg_replace( '/(\?.*)?$/', '', $_SERVER["REQUEST_URI"] );
So the request http://www.example.com/non-existent results in PHP_SELF equal to /non-existent instead of /index.php
Then in class-wp.php WP->parese_request() we have:
$self = $_SERVER['PHP_SELF']; $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' ); $home_path_regex = sprintf( '|^%s|i', preg_quote( $home_path, '|' ) ); // Trim path info from the end and the leading home path from the // front. For path info requests, this leaves us with the requesting // filename, if any. For 404 requests, this leaves us with the // requested permalink. $req_uri = str_replace($pathinfo, '', $req_uri); $req_uri = trim($req_uri, '/'); $req_uri = preg_replace( $home_path_regex, '', $req_uri ); $req_uri = trim($req_uri, '/'); $pathinfo = trim($pathinfo, '/'); $pathinfo = preg_replace( $home_path_regex, '', $pathinfo ); $pathinfo = trim($pathinfo, '/'); $self = trim($self, '/'); $self = preg_replace( $home_path_regex, '', $self ); $self = trim($self, '/');
This ends up with: $req_uri = $self = non-existent
Later in the same function we have:
// If req_uri is empty or if it is a request for ourself, unset error. if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) { unset( $error, $_GET['error'] ); if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) unset( $perma_query_vars ); $this->did_permalink = false; }
Here because $req_uri == $self this code unsets the 404 which subsequently results in the front page being loaded rather than the 404 page
I did some further investigation and this appears to only happen on multisite installs. If I take a normal wordpress install and set $_SERVER['PHP_SELF'] = '' the 404's work as normal.
Attachments (2)
Change History (10)
This ticket was mentioned in Slack in #core by jorbin. View the logs.
10 years ago
This ticket was mentioned in Slack in #core-multisite by flixos90. View the logs.
10 years ago
#6
@
9 years ago
I've added unit tests. This is my first time writing WordPress core unit tests. Feedback is welcome.
#8
@
8 years ago
Noting that I hit this issue (also logged here https://github.com/laravel/valet/issues/526) when using Valet due to its default population of PHP_SELF.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
I had a different issue which however has the same cause, i.e the fact that
$_SERVER['PHP_SELF']does not contain a reliable value depending on the server configuration.In most cases, it contains the relative path of the file to boot WordPress. When the server does not set it,
wp_fix_server_vars()fixes it with$_SERVER['REQUEST_URI']without the query string.This works well for admin, login, etc... but not when WordPress boots from
index.php. In that case, it is expected that the server fills the variable with'/index.php'. But if the server does not fill it, thenwp_fix_server_vars()fixes it incorrectly to something like'/'when using default permalinks or something like'/postname'when using pretty permalinks.This inconsistency can lead to various potential bugs including the one reported here.
In 34634.diff, I propose to explicitely set
$_SERVER['PHP_SELF']to'/index.php'when it is empty and$_SERVER['REQUEST_URI']does not contain a php file. This is a first rough patch which may need a smarter test to avoid some edged cases.