Search code examples
.htaccessurl-rewriting

Trailing Slash Not Being Added to My .htaccess Rewrite


The rules below are accomplishing what I want, i.e., www to non-www and http to https, however, I have two remaining issues:

Trailing slash should be added to all pages (except pages that end with any type of modifier, e.g., /page#foo, page/foo.jpg, etc.

Here are the current rules in my .htaccess file

RewriteEngine On
RewriteCond %{HTTP_HOST} !=localhost
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]

Solution

  • The rules below are accomplishing what I want, i.e., www to non-www and http to https

    The rule you posted does not specifically redirect HTTP to HTTPS, since it won't redirect http://example.com/ (HTTP + non-www) requests. If you are seeing this redirect then something else is doing it.

    RewriteCond %{HTTP_HOST} !=localhost
    RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
    RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
    

    Incidentally, the first condition that checks that the Host header is not localhost is entirely superfluous since that must be true if the second condition is true. The two conditions are mutually inclusive.

    Trailing slash should be added to all pages (except pages that end with any type of modifier, e.g., /page#foo, page/foo.jpg, etc.)

    It's impossible to detect fragment identifiers (aka hash) (ie. the foo in /page#foo) in .htaccess since this part of the URL (ie. #foo) is not sent to the server as part of the HTTP request. The #foo part of the URL is only used by client-side code/JavaScript. As far as the server is concerned, there is no difference between /page#foo and /page. If this is an issue for you then you can't force the trailing slash in .htaccess. However, the trailing slash would be added after /page not after /page#foo (if that is what you were thinking), ie. /page#foo would be redirected to /page/#foo, so this shouldn't be an issue.

    You can add the following rule before your existing rule, just after the RewriteEngine directive:

    # Append trailing slash to URL-path if omitted unless it looks like an actual file
    RewriteCond $1 !\.\w{2,4}$
    RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+?)\.?$ [NC]
    RewriteRule ^(.*[^/])$ https://%1/$1/ [R=301,L]
    

    The RewriteRule pattern ^(.*[^/])$ matches any URL-path that does not already end with a slash. The URL-path is then captured in the $1 backreference. The first condition (RewriteCond directive) then checks that the URL-path does not end with what looks-like a file extension (ie. a dot followed by between 2 and 4 alphanumeric upper/lowercase characters). The second condition then just captures the domain name less any optional www. prefix, saving this in the %1 backreference.

    This also redirects to HTTPS and non-www domain name, so it avoids a potential second redirect by the rule that follows.

    Test first with a 302 (temporary) redirect to avoid potential caching issues.

    If you are testing on localhost and localhost does not support HTTPS then modify the rule as follows in order to preserve HTTP on localhost only. For example:

    RewriteCond $1 !\.\w{2,4}$
    RewriteCond %{HTTP_HOST} ^()(localhost)$ [OR]
    RewriteCond s%{HTTP_HOST} ^(s)(?:www\.)?(.+?)\.?$ [NC]
    RewriteRule ^(.*[^/])$ http%1://%2/$1/ [R=301,L]