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?

  • 1
    move the redirect to the front. A Rule with a [L] at he end results that the lines after it are not evaluated anymore. – Gerald Schneider Apr 24 '18 at 14:18

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.


Ideally you have access to the server configuration and then you don't need to do your redirects from a .htaccess file and yours becomes a text book case on when NOT to use mod_rewrite where you configure the redirects on specific VirtualHost entries.

<VirtualHost *:80>
   # Redirect http://[www.]example.com/login to https://www.example.com/login
    ServerName www.example.com
    ServerAlias example.com
    Redirect "/" "https://www.example.com/"

<VirtualHost *:443>
    # Redirect https://example.com/login to https://www.example.com/login
    ServerName example.com
    Redirect "/" "https://www.example.com/"

<VirtualHost *:443>
    ServerName www.example.com
    DocumentRoot /var/www/...

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.