6

I have an issue with Apache passing to the PHP $_SERVER['REQUEST_URI'] variable the URL after it has been rewritten rather than the original one requested.

I am doing this rewriting because I had a WordPress website and wanted to move it to a subdirectory rather than having it in a root path, but still wanted to keep its URL to be the root URL.

This does not happen consistently. If I request www.xyz.com/wp-admin it populates the PHP REQUEST_URI variable with www.xyz.com/wordpress/wp-admin (which is the URL after it has been rewritten), but if I request www.xyz.com/wp-admin/ (with a trailing slash) it actually populates the PHP REQUEST_URI variable with www.xyz.com/wp-admin/ (the original URL, before the rewriting). What I want is for the REQUEST_URI to be populated with the URL before it has been rewritten.

My .htaccess file is below:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www.)?xyz.com$
RewriteCond %{REQUEST_URI} !^/wordpress/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /wordpress/$1
RewriteCond %{HTTP_HOST} ^(www.)?xyz.com$
RewriteRule ^(/)?$ wordpress/index.php [L]
</IfModule>`

PHP version is 5.3. Apache version is 2.4 (Win32).


UPDATE: I looked into it more and when I type in the URL www.xyz.com/wp-adminthen there is a 301 redirect first to www.xyz.com/wordpress/wp-admin/ but this does not happen for www.xyz.com/wp-admin/ (with a trailing slash). For the one with the trailing slash there is only the rewrite, as expected. So the question now is why the 301 redirect happens in the first place for the URL without the trailing slash.

To clarify, there is no actual folder /wp-admin/, but there is a folder /wordpress/wp-admin/.

MrWhite
  • 12,647
  • 4
  • 29
  • 41
Mg512
  • 61
  • 1
  • 3
  • tbh this does not make sense. If indeed apache is redirecting it, PHP should detect the REQUEST_URI as the final redirected one. How are you validating what PHP is seeing as the REQUEST_URI ? – user16081-JoeT Feb 21 '17 at 01:31
  • Can you clarify what URL you are ultimately seeing in the browsers address bar when you request `/wp-admin` (no trailing slash)? – MrWhite Feb 21 '17 at 01:33
  • 1
    @user16081-JoeT I validated it using XDebug. What I did not notice before though is that before I even see the request there was apparently alkready a 301 redirect to www.xyz.com/wordpress/wp-admin/. Please see my update to the question which I made after seeing your comment – Mg512 Feb 21 '17 at 01:54
  • @w3dk I didn't write this as I did not want to overcomplicate the question, but the script in `wp-admin/index.php` itself does a 302 redirect. The URL it redirects to depends on the `REQUEST_URI` variable and when that is set incorrectly then the 302 redirect is incorrect. Eventually after the 302 redirect has happened, then for the URL `www.xyz.com/wp-admin` I would then see `www.xyz.com/wp-login.php?redirect_to=http%3A%2F%2Fxyz.com%2Fwordpress%2Fwp-admin%2F&reauth=1`. – Mg512 Feb 21 '17 at 02:00
  • @w3dk For the URL with the trailing slash (`www.xyz.com/wp-admin/`) I would see `www.xyz.com/wp-login.php?redirect_to=http%3A%2F%2Fxyz.com%2Fwp-admin%2F&reauth=1` which is what I want. – Mg512 Feb 21 '17 at 02:00

1 Answers1

2

The "problem" is related to mod_dir (although mod_dir isn't strictly "the" problem - it's doing the correct thing and "fixing" the URL).

Since wp-admin is a physical directory, mod_dir "fixes" the URL by appending a trailing slash (required in order to correctly serve the directory index, eg. index.php). It does this with a 301 redirect. The problem is that this is occurring after your internal rewrite which results in the rewrite being turned into a redirect, thus exposing your /wordpress subdirectory.

So, the correct URL is strictly /wp-admin/ (with a trailing slash). If you didn't have your /wordpress subdirectory and everything was in the document root then mod_dir would simply redirect /wp-admin to /wp-admin/, which would then result in /wp-admin/index.php being served (as an internal subrequest).

What you need to do is append the trailing slash manually. If the requested URL does not end in a slash but would otherwise map to a physical directory within the /wordpress subdirectory then append the trailing slash (via an external redirect). This redirect should occur before your internal rewrites.

So, try the following before your existing directives:

# Append a slash if it is omitted and would map to a directory
RewriteCond %{REQUEST_URI} !^/wordpress/
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{DOCUMENT_ROOT}/wordpress/$1 -d
RewriteRule (.*) /$1/ [R=302,L]

What this does is, for any request that does not already start /wordpress/ and does not end in a slash but does map to a physical directory within the /wordpress subdirectory then redirect and append a slash. So, a request for /wp-admin gets redirected /wp-admin/, providing /wordpress/wp-admin exists as a directory. (Your internal rewrites then route the URL as before.)

You will need to make sure your browser cache is cleared (or test with the browsers object inspector open and disable the cache) since the earlier 301 redirects (by mod_dir) will have been cached.

When you are sure it's working OK then change the 302 (temporary) redirect to a 301 (permanent) - if that is the intention. 302s are cached by the browser, so makes testing that bit easier.


Aside:

Apache's REQUEST_URI vs PHP's $_SERVER['REQUEST_URI']

...Apache passing to the PHP $_SERVER['REQUEST_URI'] variable the URL after it has been rewritten rather than the original one requested.

Apache doesn't actually pass the REQUEST_URI variable from mod_rewrite to PHP directly. Whilst these two variables have the same name, as far as I can tell, PHP itself populates this variable from the request.

The Apache REQUEST_URI server variable and PHP's $_SERVER['REQUEST_URI'] superglobal actually contain different information:

  • PHP's $_SERVER['REQUEST_URI'] contains the query string, Apache's REQUEST_URI server variable does not; it contains the URL-path only.
  • PHP's $_SERVER['REQUEST_URI'] contains the original URL-path from the request, not the rewritten URL. Whereas Apache's REQUEST_URI server variable is updated (throughout the request) to contain the rewritten URL.
  • The URL-path component of $_SERVER['REQUEST_URI'] is not URL decoded (ie. %-decoded). Whereas the Apache REQUEST_URI server variable is %-decoded. (NB: The query string portion of the URL always remains %-encoded in both Apache and PHP. If you need to examine the %-encoded URL-path in Apache then check THE_REQUEST Apache server variable.)
MrWhite
  • 12,647
  • 4
  • 29
  • 41