phpapache.htaccessmod-rewrite

htaccess remove trailing slash on root directory redirecting too many times


I'm using my index.php file to handle all requests to my domain (except of course, css, js etc and files that actually exist). That works perfectly. What I want now is remove all trailing slashes so that mydomain.com/folder/file/ goes to mydomain.com/folder/file. I came across this solution

RewriteCond %{REQUEST_URI} /(.*)/$
RewriteRule ^ /%1 [R=301,L]

here: Htaccess: add/remove trailing slash from URL I see it as a very elegant solution, it actually works but here's is what I find strange:

Here's the rest of my htaccess file. Pasting everything just incase one of the commands is affecting or overriding the other, I'm not a htaccess expert please. Thanks.

RewriteEngine On
RewriteCond %{REQUEST_URI} /(.*)/$
RewriteRule ^ /%1 [R=301,L] #added slash here too, don't forget it

RewriteCond %{SERVER_PORT} 80
RewriteCond %{HTTP_HOST} ^example\.net$ [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.net$
RewriteRule ^(.*)$ https://example.net/$1 [R=301,L]

RewriteCond %{HTTP_HOST} www.example.net
RewriteRule (.*) https://example.net/$1 [R=301,L]

#RewriteRule ^(.*)index\.(php|html?)$ /$1 [R=301,NC,L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [L,QSA]

#Header set X-Frame-Options "ALLOW-FROM https://whatismyscreenresolution.net/"

Redirect 301 "/brand" "/brands"

ErrorDocument 404 /404.php
ErrorDocument 502 /502.shtml
ErrorDocument 504 /504.shtml
ErrorDocument 508 /508.shtml

<Files ~ "\.env$">  
Order Allow,Deny
Deny from All
</Files>

<Files .htaccess>
Order allow,deny
Deny from All
</Files>

Options -Indexes

<filesMatch ".(jpg|jpeg|png|gif|ico|webp|js)$">
Header set Cache-Control "max-age=2592000, public"
</filesMatch>

<filesMatch ".(css|js)$">
Header set Cache-Control "max-age=60, public"
</filesMatch>

Solution

  • but localhost/mydomain.com/ simply says 'localhost redirected you too many times'. How do I get the redirect to work fine both on a live server and localhost?

    This is not the "root directory" as you've stated in the question title; it's a subdirectory and that's the problem...

    RewriteCond %{REQUEST_URI} /(.*)/$
    RewriteRule ^ /%1 [R=301,L]
    

    The rule already excludes the root directory - as it should. (This is achieved because the regex /(.*)/$ would fail to match the root).

    The problem with this rule is you are trying to remove the trailing slash from every other URL, including subdirectories (as in physical filesystem directories). You can't do this - directories need the trailing slash*1. You would have the same problem on the live server if you requested a subdirectory. (I assume you do have subdirectories for assets, images, etc.?)

    If you remove the trailing slash then mod_dir will (by default) append the slash again with a 301 redirect - resulting in an endless redirect loop.

    (*1 You can suppress the trailing slash on directories, in terms of the URL you see - but the trailing slash still needs to be present in order to serve directory index documents from those directories. In order to achieve this you can internally rewrite the request to append the trailing slash - but this is messy - particularly when resolving canonicalization issues - and probably not necessary in your situation anyway.)

    It's a simple fix... you basically need to check that the request does not map to a directory before removing the trailing slash.

    Try the following instead:

    # Removing trailing slashes from non-directory URLs
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond ${REQUEST_URI} ^/(.+)/$
    RewriteRule ./$ https://example.net/%1 [R=301,L]
    

    Note that I've also included the scheme+hostname in the substitution string (as you have in the other canonical redirects) in order to minimise the number of redirects when also requesting HTTP and/or www (although these should be edge cases anyway).

    You will need to clear your browser (and possibly other intermediary) caches before testing and test first with a 302 (temporary) redirect to avoid potential caching issues. 301 (permanent) redirects (including errors) are cached persistently by the browser.

    Aside:

    On localhost, localhost/mydomain.com/folder/file/ redirects fine,

    Instead of simply using a subdirectory off the localhost, why don't you define the <VirtualHost> ServerName and DocumentRoot properly for mydomain.com - or perhaps use a subdomain like local.mydomain.com if you need to keep mydomain.com (the live site) accessible? Then you have the same directory structure on both local and live sites.

    (It could still be stored as a subdirectory off the localhost's document root if you wish, but the files could literally be stored anywhere.)