254

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

1
  • I found a mod_rewrite solution that works well for both proxied and unproxied servers. See the answer at stackoverflow.com/a/34065445 Commented May 3, 2016 at 13:54

10 Answers 10

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();
    }
}
Sign up to request clarification or add additional context in comments.

11 Comments

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.
When I use the mod_rewrite method, I get sent to https but with a "The page isn't redirecting properly" error.
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.
If you experience redirect loops on servers running behind proxies (CloudFlare, Openshift), see this answer for a solution that works for that case too.
@GTodorov - 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".
|
36

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]

6 Comments

out of curiosity, why the RewriteCond %{REQUEST_METHOD} !^POST$ ?
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).
@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: The line RewriteCond %{REQUEST_METHOD} !^POST$ should prevent POST submissions from being affected by these redirections.
I like this PHP version. It is much better as it considers a POST and handles better if headers have already been sent.
|
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.

Comments

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/

Comments

5

Simple and Easy , just add following

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

2 Comments

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.
I had a similar problem and I had to apply the rules to the https.conf file. See my answer below.
0

This code works for me

RewriteEngine On
RewriteBase /
RewriteCond %{HTTP:X-HTTPS} !1
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

Comments

0

I wrote this into .htaccess file, it works

RewriteEngine on
SetEnvIf X-Forwarded-Proto https SERVER_PORT=443
SetEnvIf X-Forwarded-Proto https HTTPS=on
RewriteCond %{HTTP:HTTPS} !=on [NC]
RewriteRule ^(.*)$ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]

Comments

0

I tried below for force redirect , its works for all the three conditions

http://example.org => https://www.example.org,

http://www.example.org => https://www.example.org

https://example.org => https://www.example.org

RewriteEngine On

# Redirect http://example.org to https://www.example.org

RewriteCond %{HTTPS} off

RewriteCond %{HTTP_HOST} !^www\. [NC]

RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Redirect http://www.example.org to https://www.example.org

RewriteCond %{HTTPS} off

RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Redirect https://example.org to https://www.example.org

RewriteCond %{HTTPS} on

RewriteCond %{HTTP_HOST} !^www\. [NC]

RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Comments

-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

1 Comment

NO! The site URL needs to be dynamic.
-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]
    

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.