If you are on Apache 2.4 then in .htaccess
you can use mod_rewrite with an Apache expression and make use of the tolower()
function. (This doesn't require the use of a RewriteMap
in the server config, as mentioned in comments.) For example:
RewriteEngine On
RewriteCond expr "tolower(%{REQUEST_URI}) =~ /(.*)/"
RewriteRule [A-Z] %1 [L]
The RewriteRule
pattern simply checks there is at least one uppercase letter in the requested URL-path. The RewriteCond
directive then calls the tolower()
function on the URL-path (REQUEST_URI
server variable) which is then effectively captured using the regex. The %1
backreference in the substitution string then holds the result of the tolower()
function call, ie. the lowercased URL-path, which is internally rewritten to.
To "correct" the URL and issue an external redirect, then just add the R
flag to the RewriteRule
directive. For example:
:
RewriteRule [A-Z] %1 [R=301,L]
UPDATE: To eliminate a double redirect when redirecting HTTP to HTTPS (and/or non-www vs www) then include the full canonical URL as part of this rule and implement the canonical (scheme + hostname) redirects second.
For example:
# 1 - Upper to lowercase conversion (and HTTPS and WWW)
RewriteCond expr "tolower(%{REQUEST_URI}) =~ /(.*)/"
RewriteRule [A-Z] https://www.example.com%1 [R=301,L]
# 2 - HTTP to HTTPS
:
Note that the %1
backreference already includes the slash prefix at the start of the URL-path, so this is omitted in the substitution string.
HOWEVER, it is not necessarily incorrect to have a double redirect in this situation. ie. Redirect HTTP to HTTPS (same hostname and URL-path) first then canonicalise other elements of the requested URL (hostname, upper/lowercase URL-path etc.) second. These should be edge cases to begin with, so the real-world impact is minimal.
Note that if you are implementing HSTS then it is a requirement that you first redirect from HTTP to HTTPS on the same hostname, before canonicalising the hostname (ie. www vs non-www). In this case you should use the HTTP_HOST
server variable (ie. %{HTTP_HOST}
) as the hostname in the above redirect. A double redirect cannot be avoided in this scenario.