254

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

lpd
  • 2,151
  • 21
  • 29
Sanjay Shah
  • 2,809
  • 2
  • 19
  • 20
  • I found a `mod_rewrite` solution that works well for both proxied and unproxied servers. See the answer at https://stackoverflow.com/a/34065445/ – raphinesse May 03 '16 at 13:54

8 Answers8

466

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();
    }
}
UKB
  • 1,162
  • 1
  • 11
  • 15
Gordon
  • 312,688
  • 75
  • 539
  • 559
  • 1
    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. – Michael J. Calkins Sep 24 '14 at 23:11
  • 1
    When I use the mod_rewrite method, I get sent to https but with a "The page isn't redirecting properly" error. – Czechnology Oct 09 '14 at 12:00
  • 11
    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. – Czechnology Oct 09 '14 at 12:47
  • Don't forget to set `AllowOverride all` in your apache httpd.conf; otherwise the rewriting rules in your .htaccess will be ignored – cnlevy Aug 02 '15 at 14:49
  • 8
    If you experience **redirect loops** on servers running behind proxies (*CloudFlare*, *Openshift*), see [this answer](http://stackoverflow.com/a/34065445/380229) for a solution that works for that case too. – raphinesse Dec 03 '15 at 11:58
  • 1
    It should be "RewriteRule ^https"... NOT "RewriteRule ^ https". Remove the space, otherwise you may get redirect issue. – GTodorov May 22 '16 at 23:36
  • Didn't work for me. I got "too many redirects" error. Then I followed instructions at https://support.cloudways.com/what-can-i-do-with-an-htaccess-file/ and it worked. – Ryan Jun 18 '16 at 20:43
  • 4
    @GTodorov - Removing the space broke the rewriterule for me. From my (limited) knowledge of rewriterules, the syntax is `RewriteRule `. Thus, the space needs to be there, and the single `^` just says "match all input URLs". – Sphinxxx Jun 30 '16 at 13:37
  • @Sphinxxx Sorry about that, I don't know what I was thinking at that time. You're right! – GTodorov Jul 02 '16 at 01:22
  • For some reason when I did this, it made `$_POST` always empty – Ray Mar 15 '19 at 16:55
  • Can also add this line in case you ever run this site locally for testing. `RewriteCond %{REMOTE_ADDR} !^127\.0\.0\.1$` – William Entriken Nov 23 '19 at 16:40
38

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]
Luke Stevenson
  • 10,357
  • 2
  • 26
  • 41
  • out of curiosity, why the `RewriteCond %{REQUEST_METHOD} !^POST$` ? – depoulo Jun 29 '13 at 06:43
  • 13
    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). – Luke Stevenson Jun 30 '13 at 06:35
  • @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? – StackOverflowNewbie Jul 08 '14 at 19:23
  • 1
    @StackOverflowNewbie: The line `RewriteCond %{REQUEST_METHOD} !^POST$` should prevent POST submissions from being affected by these redirections. – Luke Stevenson Jul 09 '14 at 06:15
  • I like this PHP version. It is much better as it considers a POST and handles better if headers have already been sent. – TheStoryCoder Feb 29 '16 at 10:38
  • 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') )` – TheStoryCoder Feb 29 '16 at 13:01
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.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Amit Verma
  • 40,709
  • 21
  • 93
  • 115
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/
Community
  • 1
  • 1
lpd
  • 2,151
  • 21
  • 29
4

Simple and Easy , just add following

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Saurabh Mistry
  • 12,833
  • 5
  • 50
  • 71
  • 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. – ThN Dec 14 '18 at 13:53
  • I had a similar problem and I had to apply the rules to the https.conf file. See my answer below. – Risteard Jul 02 '19 at 10:19
0

This code works for me

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP:X-HTTPS} !1
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]
Alexufo
  • 1,723
  • 1
  • 15
  • 30
-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

sephrrr
  • 83
  • 1
  • 14
-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]
    
Kashif Latif
  • 657
  • 3
  • 15
  • 29