Issues with .htaccess

Trying to override current directory with Apache config.

Posted on: 220124

I've encountered an interesting issue with my .htaccess files, here are the symptoms...

I have a folder named dir

Now, I also route all request URIs that are not a file through index.php. So requests to /dir/image.jpg should display the image in question, but a request specifically to /dir should route to the index in the root directory with the request made as a query string. So, /dir becomes /index.php?dir.

This almost works.

It works in that it displays the index from my root directory, but it prepends the folder name to the request.

  • Request sent: diagonalsymmetry.com/dir
  • What I want: diagonalsymmetry.com/?dir
  • What I'm getting: diagonalsymmetry.com/dir/?dir

This is frustrating, as not only do I not get the request I want, but the 404 page doesn't load properly as the current directory has now changed. I could use absolute paths for all my scripts and stylesheets, but I would rather get to the root of the problem.

I have tried a number of different configurations in the .htaccess file so far...

RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.*) index.php?$1 [L] RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.*) %{DOCUMENT_ROOT}/index.php?$1 [L]

All to no avail.

Redirect Cache

Curiously, the issue persisted even after removing all htaccess rules which point to index.php, and even after removing .htaccess from the directory! Something is amiss. This is when I realized the change from /dir to /dir/?dir was being cached by my browser. This explains why changing the htaccess rules wasn't working. I cleared the cache in Firefox by pressing ctrl+shift+del, other browsers may vary.

Next, to test each rule to find the offender. I have three sets of rules which handle all requests that don't have special prefixes...

RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.*) %{DOCUMENT_ROOT}/index.php?$1 [L] RewriteCond %{REQUEST_FILENAME} -f RewriteRule (.*) - [L] RewriteRule (.*) %{DOCUMENT_ROOT}/index.php?$1 [L]

Testing each of these individually—clearing the cache in between—I discovered the first and third sets of rules were causing the issue.

Redirect to Domain

I finally found out the issue was with the %{DOCUMENT_ROOT} environment variable. It seemed to point to the current directory, which is counter-intuitive, since I expect it to point to the root of the server regardless of the current directory. The solution was to include my site's domain in the rule...

RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.*) https://diagonalsymmetry.com/index.php?$1 [L] RewriteRule (.*) https://diagonalsymmetry.com/index.php?$1 [L]

This fixes the issue with the folder appearing in the path, but creates two new problems. First issue, the rule requires the domain to be listed in the .htaccess file. This is less than ideal, as the object here is to create a universal .htaccess that can be deployed on any domain. I would have to generate the .htaccess for each project in this case. And it gets particularly tiresome when testing sites on localhost/ then deploying to server. This won't do.

RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.*) http://%{HTTP_HOST}/index.php?$1 [L] RewriteRule (.*) %{DOCUMENT_ROOT}/index.php?$1 [L]

Fortunately, there's an environment variable available called HTTP_HOST which fixes this first issue. Using this variable, you needn't specify the domain of the site in the .htaccess file. And I also discovered it is only needed when the request is a directory, so the second rule can just use DOCUMENT_ROOT for requests that do not map to a folder.

The second issue created by this approach is that the redirect is no longer hidden. The URL in the address bar changes from site.com/dir to site.com/index.php?dir, even for requests to the server root. This is not the end of the world, but it is irritating. Let's see if we can solve that issue...

RewriteRule ^/?$ %{DOCUMENT_ROOT}/index.php [L]

Placing this rule before the other three will prevent requests to root from showing index.php. This does not solve the issue of requests that map to a subfolder not mapping silently, though. This is only a problem if the request made is a valid directory on the server.

Conclusion

So far as I have gotten on this issue, I have managed to make requests that would otherwise point to a folder point to the index file, but not silently. They show index.php?dir, while requests that do not match a folder do not. The index file itself loads silently without showing index.php, though.

These rules achieved this result and will work on any domain...

RewriteRule ^/?$ %{DOCUMENT_ROOT}/index.php [L] RewriteCond %{REQUEST_FILENAME} -d RewriteRule (.*) http://%{HTTP_HOST}/index.php?$1 [L] RewriteCond %{REQUEST_FILENAME} -f RewriteRule (.*) - [L] RewriteRule (.*) %{DOCUMENT_ROOT}/index.php?$1 [L]

The solution to folder requests not mapping silently would be to not feature any links that map to folder names on the server! Not a total solution, I'll make another update if I manage to solve that final annoyance.