Apache's mod_rewrite and PHP's REQUEST_URI variable

Solution 1:

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.)