2

I have the following .htaccess file:

AddDefaultCharset utf-8
RewriteEngine on
Options +SymLinksIfOwnerMatch
RewriteBase /

# redirect all www-requests to no-www
# -
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]

# redirect all home pages to / (root)
# -
RewriteCond %{THE_REQUEST} ^.*/index\.(php|html?)
RewriteRule ^(.*)index\.(php|html?)$ /$1 [R=301,L] 

# remove trailing slash from dirs
# -
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)/$ /$1 [R=301,L]

# automatically add index.php when needed
# -
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|login\.php|reg\.php|robots\.txt|css/|js/)
RewriteRule ^(.*)$ /index.php [L]

The .htaccess file should do the following (for SEO):

  1. Conversion to no-www (http://www.example.com should become http://example.com)
  2. All URIs with trailing slashes should convert to no-trailing-slash: http://example.com/me/ should be redirected http://example.com/me
  3. All URIs with index.php/index.html should convert to just nothing: http://example.com/admin/index.php or http://example.com/admin/ should be eventually displayed as http://example.com.

However the current version of .htaccess results in a cyclic redirection when trying to access http://example.com/admin. The real document that should be fetched by the browser is http://example.com/admin/index.php.

Can anyone please help me with this issue?

1

cyclic redirection when trying to access http://example.com/admin. The real document that should be fetched by the browser is http://example.com/admin/index.php

Since /admin is a physical directory, mod_dir will "fix" the URL by appending a trailing slash. This is achieved via a 301 redirect.

However...

# remove trailing slash from dirs
# -
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)/$ /$1 [R=301,L]

These directives remove the trailing slash on anything that isn't a file (which naturally includes directories). This is also achieved via a 301 redirect.

Hence the "cyclic redirection" / redirect loop.

Arguably, you shouldn't be removing the trailing slash from physical directories (despite the code comment). You can certainly remove the trailing slash from URL-path segments (or "files") that perhaps "look like" directories, but not from physical filesystem directories (OK, you can - but it requires more work). I'm not sure why you would want to keep the trailing slash on physical files (unless you are using PATH_INFO - but there is no evidence of that here - so it looks like a mistake). So, to fix this you simply need to replace the !-f (not a file) check with !-d (not a directory) check. For example:

# Remove trailing slash from everything except physical directories
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [R=301,L]

Remove the trailing slash from physcial directories

You can remove the trailing slash from physical directories, but it requires more work and diligence as you are really going against how the filesystem works. Physical directories need a trailing slash.

To remove the trailing slash on physical directories, you need to:

  1. Prevent mod_dir from "fixing" the URL by appending a trailing slash to directories:

    DirectorySlash Off
    
  2. Remove (redirect) the trailing slash on any URL that is requested directly (excluding rewritten requests, as in #3):

    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ^(.*)/$ /$1 [R=301,L]
    
  3. Append (rewrite) the trailing slash on any directory where it is absent:

    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule !/$ %{REQUEST_URI}/ [L]
    

Note also the security warning in the Apache docs with regards to the DirectorySlash directive:

Security Warning
Turning off the trailing slash redirect may result in an information disclosure. Consider a situation where mod_autoindex is active (Options +Indexes) and DirectoryIndex is set to a valid resource (say, index.html) and there's no other special handler defined for that URL. In this case a request with a trailing slash would show the index.html file. But a request without trailing slash would list the directory contents.

|improve this answer|||||
0

I'm familiar with regex, though not an expert, but it looks like you might be getting a loop from the conditions under # redirect all home pages to / (root) and # automatically add index.php when needed

It looks like the last bit may be redirecting http://example.com/admin to http://example.com/admin/index.php, which tries to load and is then redirected back to http://example.com/admin by the earlier bit.

I think you don't need the last bit. Unless your server is configured improperly, http://example.com/admin should automatically fetch the document at http://example.com/admin/index.php

|improve this answer|||||
  • 1
    This is not what is triggering the redirect loop. The "last bit" wouldn't touch a request for /admin since it is a physical directory (the preceding condition checks this). (If it did do anything it would rewrite the request to /index.php in the document root - this is the "front-controller" and is probably essential to the working of the website). The "earlier bit" only works on direct requests (by checking against THE_REQUEST), not rewritten requests (as in the "last bit") - so this avoids a redirect loop. – MrWhite Dec 14 '18 at 1:05
  • 1
    "/admin should automatically fetch the document at /admin/index.php" - Yes. However, mod_dir first issues a 301 redirect to /admin/ (appends a slash to the physical directory) - and this is the start of the redirect loop, because the "middle bit" then removes it again (with another 301 redirect). etc. etc. I've explained more in my answer. – MrWhite Dec 14 '18 at 1:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.