Htaccess redirect non www & HTTP with full URL

.htaccessapache-2.4redirect

I Have these .htaccess rules:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}

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

It works fine when redirecting HTTP to HTTPS & non-www to www on the main domain, but if a user goes directly to a specific URL, I want to redirect to https, www with the full URL path.

For example:

  • http://example.com/login should redirect to https://www.example.com/login
  • http://www.example.com/login should redirect to https://www.example.com/login
  • https://example.com/login should redirect to https://www.example.com/login

With the current settings it only redirects to https://www.example.com or https://www.example.com/index.php, how can I achieve that?

Best Answer

As @GeraldSchneider mentioned in comments, your directives are in the wrong order. The external redirects (HTTP to HTTPS and non-www to www) need to go before your front-controller as otherwise they will simply never be processed for anything other than actual file requests. (Your front-controller routes all unknown requests to index.php and then stops.)

However, your redirects are also incorrect. You are missing the "full URL" from the substitution, so as it stands they will always redirect back to the document root.

So, try something like the following instead:

RewriteEngine On

# Front-controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L]

# HTTP to HTTPS redirect
RewriteCond %{HTTPS} !on
RewriteRule (.*) https://%{HTTP_HOST}/$1 [R=301,L]

# Non-www to www redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule (.*) http://www.%{HTTP_HOST}/$1 [R=301,L]

Notes regarding the above directives:

  • In the front-controller, there is no need to capture the URL-path (ie (.*)) if it is not being used in the substitution. The QSA flag is not required here also - the query string from the request is appended by default.
  • Note the $1 in the RewriteRule susbstitution - this is a backreference to the captured URL-path from the request (ie. the "full URL"). This was missing from both your redirects.
  • You were missing the [R=301,L] flags from the HTTP to HTTPS redirect. Without explicitly stating the status code this would have resulted in a temporary 302 redirect and rewriting would have continued. If you intend to implement HSTS later then these redirects need to be separate.

However, if you have access to the server config and you do not intend to implement HSTS then refer to @HBruijn's answer instead.