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-admin
then 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/
.
Best Answer
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:
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 a301
(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 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:$_SERVER['REQUEST_URI']
contains the query string, Apache'sREQUEST_URI
server variable does not; it contains the URL-path only.$_SERVER['REQUEST_URI']
contains the original URL-path from the request, not the rewritten URL. Whereas Apache'sREQUEST_URI
server variable is updated (throughout the request) to contain the rewritten URL.$_SERVER['REQUEST_URI']
is not URL decoded (ie. %-decoded). Whereas the ApacheREQUEST_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 checkTHE_REQUEST
Apache server variable.)