Force SSL/https using .htaccess and mod_rewrite
Asked Answered
C

8

254

How can I force to SSL/https using .htaccess and mod_rewrite page specific in PHP.

Capricorn answered 9/12, 2010 at 13:56 Comment(1)
I found a mod_rewrite solution that works well for both proxied and unproxied servers. See the answer at stackoverflow.com/a/34065445Marileemarilin
K
467

For Apache, you can use mod_ssl to force SSL with the SSLRequireSSL Directive:

This directive forbids access unless HTTP over SSL (i.e. HTTPS) is enabled for the current connection. This is very handy inside the SSL-enabled virtual host or directories for defending against configuration errors that expose stuff that should be protected. When this directive is present all requests are denied which are not using SSL.

This will not do a redirect to https though. To redirect, try the following with mod_rewrite in your .htaccess file

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

or any of the various approaches given at

You can also solve this from within PHP in case your provider has disabled .htaccess (which is unlikely since you asked for it, but anyway)

if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
    if(!headers_sent()) {
        header("Status: 301 Moved Permanently");
        header(sprintf(
            'Location: https://%s%s',
            $_SERVER['HTTP_HOST'],
            $_SERVER['REQUEST_URI']
        ));
        exit();
    }
}
Known answered 9/12, 2010 at 14:16 Comment(11)
This is great in our situation because we currently have a mixture of http and https traffic. For our admin area we just popped in the .htaccess script while keeping the rest of the site http.Corpuz
When I use the mod_rewrite method, I get sent to https but with a "The page isn't redirecting properly" error.Baggywrinkle
Followup: If you're having similar trouble, check your server and HTTP variables. If your server uses proxies, you might want to use %{HTTP:X-Forwarded-Proto} or %{HTTP:X-Real-Port} variables to check whether SSL is turned on.Baggywrinkle
Don't forget to set AllowOverride all in your apache httpd.conf; otherwise the rewriting rules in your .htaccess will be ignoredCoates
If you experience redirect loops on servers running behind proxies (CloudFlare, Openshift), see this answer for a solution that works for that case too.Marileemarilin
It should be "RewriteRule ^https"... NOT "RewriteRule ^ https". Remove the space, otherwise you may get redirect issue.Canonicate
Didn't work for me. I got "too many redirects" error. Then I followed instructions at support.cloudways.com/what-can-i-do-with-an-htaccess-file and it worked.Substantiate
@Canonicate - Removing the space broke the rewriterule for me. From my (limited) knowledge of rewriterules, the syntax is RewriteRule <input-pattern> <output-url>. Thus, the space needs to be there, and the single ^ just says "match all input URLs".Adjure
@Adjure Sorry about that, I don't know what I was thinking at that time. You're right!Canonicate
For some reason when I did this, it made $_POST always emptyTuition
Can also add this line in case you ever run this site locally for testing. RewriteCond %{REMOTE_ADDR} !^127\.0\.0\.1$Resplendence
N
37

PHP Solution

Borrowing directly from Gordon's very comprehensive answer, I note that your question mentions being page-specific in forcing HTTPS/SSL connections.

function forceHTTPS(){
  $httpsURL = 'https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
  if( count( $_POST )>0 )
    die( 'Page should be accessed with HTTPS, but a POST Submission has been sent here. Adjust the form to point to '.$httpsURL );
  if( !isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS']!=='on' ){
    if( !headers_sent() ){
      header( "Status: 301 Moved Permanently" );
      header( "Location: $httpsURL" );
      exit();
    }else{
      die( '<script type="javascript">document.location.href="'.$httpsURL.'";</script>' );
    }
  }
}

Then, as close to the top of these pages which you want to force to connect via PHP, you can require() a centralised file containing this (and any other) custom functions, and then simply run the forceHTTPS() function.

HTACCESS / mod_rewrite Solution

I have not implemented this kind of solution personally (I have tended to use the PHP solution, like the one above, for it's simplicity), but the following may be, at least, a good start.

RewriteEngine on

# Check for POST Submission
RewriteCond %{REQUEST_METHOD} !^POST$

# Forcing HTTPS
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{SERVER_PORT} 80
# Pages to Apply
RewriteCond %{REQUEST_URI} ^something_secure [OR]
RewriteCond %{REQUEST_URI} ^something_else_secure
RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

# Forcing HTTP
RewriteCond %{HTTPS} =on [OR]
RewriteCond %{SERVER_PORT} 443
# Pages to Apply
RewriteCond %{REQUEST_URI} ^something_public [OR]
RewriteCond %{REQUEST_URI} ^something_else_public
RewriteRule .* http://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
Neediness answered 9/12, 2010 at 16:4 Comment(6)
out of curiosity, why the RewriteCond %{REQUEST_METHOD} !^POST$ ?Howes
Because POST parameters aren't retained on a redirect. You can omit that line if you want to make sure that all POST submissions are secure (any unsecured POST submissions will be ignored).Neediness
@Lucanos - How to write a RewriteCond that doesn't force a redirect to either http or https when POST? My .htaccess forces HTTPS on certain specific pages, and then forces HTTP on the rest. However, on the HTTPS pages, there are forms that submit to my web root. The form specifies the action url as HTTPS. However, since the web root isn't one of those pages that are specified to force HTTPS, my .htaccess then forces a redirect -- which means the POST variables are lost. How do I prevent redirects on POST?Unsnap
@StackOverflowNewbie: The line RewriteCond %{REQUEST_METHOD} !^POST$ should prevent POST submissions from being affected by these redirections.Neediness
I like this PHP version. It is much better as it considers a POST and handles better if headers have already been sent.Evince
To be even more complete you should also check for raw POST requests in the PHP code (eg. a JSON string). So the line where you count the POST values should be written like this instead: if (count( $_POST) > 0 || file_get_contents('php://input') )Evince
K
14

Mod-rewrite based solution :

Using the following code in htaccess automatically forwards all http requests to https.

RewriteEngine on

RewriteCond %{HTTPS}::%{HTTP_HOST} ^off::(?:www\.)?(.+)$
RewriteRule ^ https://www.%1%{REQUEST_URI} [NE,L,R]

This will redirect your non-www and www http requests to www version of https.

Another solution (Apache 2.4*)

RewriteEngine on

RewriteCond %{REQUEST_SCHEME}::%{HTTP_HOST} ^http::(?:www\.)?(.+)$
RewriteRule ^ https://www.%1%{REQUEST_URI} [NE,L,R]

This doesn't work on lower versions of apache as %{REQUEST_SCHEME} variable was added to mod-rewrite since 2.4.

Katharina answered 18/2, 2017 at 17:51 Comment(0)
S
10

I'd just like to point out that Apache has the worst inheritance rules when using multiple .htaccess files across directory depths. Two key pitfalls:

  • Only the rules contained in the deepest .htaccess file will be performed by default. You must specify the RewriteOptions InheritDownBefore directive (or similar) to change this. (see question)
  • The pattern is applied to the file path relative to the subdirectory and not the upper directory containing the .htaccess file with the given rule. (see discussion)

This means the suggested global solution on the Apache Wiki does not work if you use any other .htaccess files in subdirectories. I wrote a modified version that does:

RewriteEngine On
# This will enable the Rewrite capabilities

RewriteOptions InheritDownBefore
# This prevents the rule from being overrided by .htaccess files in subdirectories.

RewriteCond %{HTTPS} !=on
# This checks to make sure the connection is not already HTTPS

RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [QSA,R,L]
# This rule will redirect users from their original location, to the same location but using HTTPS.
# i.e.  http://www.example.com/foo/ to https://www.example.com/foo/
Shaving answered 16/5, 2016 at 18:30 Comment(0)
R
4

Simple and Easy , just add following

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Resultant answered 20/7, 2018 at 12:2 Comment(2)
This is pretty much what people all over the Internet telling you to do for forcing https. However, every time I do that, my whole website becomes FORBIDDEN or You have no permission to view. something like that... What am I doing wrong? I just want to know if you have any idea before I post a question on this.Fourgon
I had a similar problem and I had to apply the rules to the https.conf file. See my answer below.Cantone
W
0

This code works for me

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP:X-HTTPS} !1
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
Wellgroomed answered 11/1, 2017 at 15:18 Comment(0)
O
-1

Simple one :

RewriteEngine on
RewriteCond %{HTTP_HOST} ^(www\.example\.com)(:80)? [NC]
RewriteRule ^(.*) https://example.com/$1 [R=301,L]
order deny,allow

replace your url with example.com

Operand answered 28/1, 2017 at 10:45 Comment(1)
NO! The site URL needs to be dynamic.Mongolia
I
-2

try this code, it will work for all version of URLs like

  • website.com
  • www.website.com
  • http://website.com
  • http://www.website.com

    RewriteCond %{HTTPS} off
    RewriteCond %{HTTPS_HOST} !^www.website.com$ [NC]
    RewriteRule ^(.*)$ https://www.website.com/$1 [L,R=301]
    
Inclinatory answered 13/9, 2017 at 7:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.