In addition to Apache/mod_rewrite, some of this behaviour can be the result of the browser and how the OS maps the request to the filesystem. But as noted in comments, a "testing tool" can also result in (incorrect/unclear) behaviour that is unique to that tool.
To clarify, this is .htaccess
in the document root (a directory context). If these rules are in the main server config then behaviour could again be different due to what is actually matched in the different contexts.
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [L,R=301]
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
And noticed that in the first there is a slash between domain and URI,
but in the second variant it's not explicitly inserted.
The $1
backreference in this example does not contain the slash prefix, so it needs to be explicitly included after the hostname in the substitution string (first example). (It doesn't contain a slash prefix, because the URL-path that is matched by the RewriteRule
pattern in .htaccess
does not contain a slash prefix.)
Whereas the REQUEST_URI
server variable always contains the full root-relative URL-path, starting with a slash. At the very minimum, this var will contain a single slash (the document root). So if you use this variable in the substitution string then you would need to omit the slash (second example).
Nothing is "automatically inserted" here.
Reference: See the Apache docs for the RewriteRule
directive under the heading "Per-directory Rewrites": https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html#rewriterule
I did some tests with a test tool and got the following results.
^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} -> https://sub.domain.com/test/folder/file.txt
^(.*)$ https://%{HTTP_HOST}/%{REQUEST_URI} -> https://sub.domain.com/test/folder/file.txt
^(.*)$ https://%{HTTP_HOST}$1 -> https://sub.domain.com/test/folder/file.txt
^(.*)$ https://%{HTTP_HOST}/$1 -> https://sub.domain.comtest/folder/file.txt
Where the variants with variables seem to correct for missing/excess
slashes, while the variants with regex don't.
There might be some inconsistencies in the "test tool" here and the results of your 3rd and 4th examples seem to be the wrong way round? However...
The first example is as expected.
The second example will result in a double slash in the resulting URL after the hostname (UPDATE: the "testing tool" is wrong in this respect*1). As mentioned above, the REQUEST_URI
server variable already contains a slash prefix. HOWEVER, when the requested URL is mapped to the file system, multiple contiguous slashes are reduced to single slashes in the file-path, so a request for https://example.com//test/folder/file.txt
will still serve the static file /test/folder/file.txt
. Examining the URL-path matched by the RewriteRule
pattern (which has already had multiple slashes reduced) sees only a single slash. However, there are still two slashes in the URL-path (and this could break some URL routers).
(*1 For some reason that testing tool always reduces multiple slashes at the start of the URL-path only. Other instances of multiple slashes in the URL-path remain unchanged. This is incorrect behaviour.)
In the 3rd and 4th examples (which appear to be reversed in the results - as mentioned) you need to include the slash prefix because there is no slash prefix on the $1
backreference in this instance.
But what's weird is that if I use only the domain, minus the URI,
^(.*)$ https://%{HTTP_HOST}/ -> https://sub.domain.com/
^(.*)$ https://%{HTTP_HOST} -> https://sub.domain.com/
The slash IS added, so it looks like the http_host variable includes a
slash regardless of whether or not a URI is included. I.E. the slash
is part of the domain name?
No, there is no trailing slash on the HTTP_HOST
server variable (the hostname used in the request).
In the case of an external redirect, if you examine the Location
header in the redirect response, there is no trailing slash after the hostname in the second example above.
However, the trailing slash after the hostname is automatically added by the browser (as part of the second/redirected request) in order to "correct" the request. There is always a slash after the hostname in a valid HTTP request (whether you see this in the browser's address bar or not).
Note that this is the slash immediately after the hostname, at the start of the URL-path. Not the slash that is sometimes appended to the end of the URL-path (the "trailing slash"). The two must not be confused.
See this related Webmasters question that asks this specific question:
And potentially even weirder; that not including a domain at all and
using only regex, DOES automatically add the slash and ends up with a
workable URL.
^(.*)$ /$1 -> http://sub.domain.com/test/folder/file.txt
^(.*)$ $1 -> http://sub.domain.com/test/folder/file.txt
This relates to external redirects, not internal rewrites. (As with the previous examples.)
In the first example (that explicitly starts with a slash - a root-relative URL) then mod_rewrite will construct an absolute URL from the request, prefixing the scheme + hostname.
The second example will only result in a valid redirect (as stated) if RewriteBase /
is defined elsewhere in the .htaccess
file. This controls the URL-path that is added back to relative substitutions at the end of the rewriting process. If RewriteBase
was not defined then this would result in a malformed redirect as it would add back the directory-prefix and a result in something like /var/www/user/public_html/test/folder/file.txt
. However, omitting the slash prefix is OK (and often preferred) when performing internal rewrites (as opposed to external redirects).
In short, using only variables automatically adds slashes, using only
one variable adds slashes, using only regex adds slashes, but using
regex after variables does not?
What you are referring to as a "regex" here is really a backreference (of the form $n
) to a captured subgroup in the preceding RewriteRule
pattern (regex). The backreference itself is not part of the regex syntax.
There is no difference between using a server variable (ie. REQUEST_URI
) and using a backreference (ie. $1
) in the substitution string, except with what they contain. It's just that in the examples above, REQUEST_URI
contains a slash prefix and the backreference does not.
To take this one step further... the URL-path matched by the RewriteRule
pattern is relative to the directory that contains the .htaccess
file (the "directory-prefix"). The directory-prefix that is first removed always ends with a slash, so the URL-path that is matched never starts with a slash.
This becomes very significant when the .htaccess
file is not in the document root. For example, if the following .htaccess
file was located at /test/folder/.htaccess
:
RewriteRule ^(.*)$ https://example.com/$1 [R,L]
RewriteRule ^ https://example.com%{REQUEST_URI} [R,L]
Given a request for /test/folder/file.txt
then:
Aside: When used in a server (or virtualhost) context, as opposed to .htaccess
(a directory context) then the two rules above would behave exactly the same. Because in a server context, the URL-path matched by the RewriteRule
pattern contains the full root-relative URL-path - the same as REQUEST_URI
.