Force SSL/https using .htaccess and mod_rewrite
Solution 1
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();
}
}
Solution 2
I found a mod_rewrite
solution that works well for both proxied and unproxied servers.
If you are using CloudFlare, AWS Elastic Load Balancing, Heroku, OpenShift or any other Cloud/PaaS solution and you are experiencing redirect loops with normal HTTPS redirects, try the following snippet instead.
RewriteEngine On
# If we receive a forwarded http request from a proxy...
RewriteCond %{HTTP:X-Forwarded-Proto} =http [OR]
# ...or just a plain old http request directly from the client
RewriteCond %{HTTP:X-Forwarded-Proto} =""
RewriteCond %{HTTPS} !=on
# Redirect to https version
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Solution 3
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]
Solution 4
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.
Solution 5
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/
Related videos on Youtube
Sanjay Shah
Updated on July 03, 2020Comments
-
Sanjay Shah almost 4 years
How can I force to SSL/https using .htaccess and mod_rewrite page specific in PHP.
-
depoulo almost 11 yearsout of curiosity, why the
RewriteCond %{REQUEST_METHOD} !^POST$
? -
Luke Stevenson almost 11 yearsBecause 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).
-
StackOverflowNewbie almost 10 years@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?
-
Luke Stevenson almost 10 years@StackOverflowNewbie: The line
RewriteCond %{REQUEST_METHOD} !^POST$
should prevent POST submissions from being affected by these redirections. -
Michael J. Calkins over 9 yearsThis 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.
-
Czechnology over 9 yearsWhen I use the mod_rewrite method, I get sent to https but with a "The page isn't redirecting properly" error.
-
Czechnology over 9 yearsFollowup: 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. -
cnlevy almost 9 yearsDon't forget to set
AllowOverride all
in your apache httpd.conf; otherwise the rewriting rules in your .htaccess will be ignored -
raphinesse over 8 yearsIf you experience redirect loops on servers running behind proxies (CloudFlare, Openshift), see this answer for a solution that works for that case too.
-
TheStoryCoder about 8 yearsI like this PHP version. It is much better as it considers a POST and handles better if headers have already been sent.
-
TheStoryCoder about 8 yearsTo 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') )
-
GTodorov almost 8 yearsIt should be "RewriteRule ^https"... NOT "RewriteRule ^ https". Remove the space, otherwise you may get redirect issue.
-
Ryan almost 8 yearsDidn'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.
-
Sphinxxx almost 8 years@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". -
GTodorov almost 8 years@Sphinxxx Sorry about that, I don't know what I was thinking at that time. You're right!
-
Urgotto over 6 yearsThis is brilliant thx! But maybe it is require to add post condition? RewriteCond %{REQUEST_METHOD} !^POST$
-
raphinesse about 6 years@Aleksej True, this breaks unencrypted POST requests. But I think this is a good thing. This way, people will notice they are doing something wrong. I guess the best thing would be to redirect them to a page that informs them on how to properly use your service.
-
RRN over 5 years@raphinesse Do you know the answer of this question: stackoverflow.com/questions/51951082/…
-
Amir Savand over 5 yearsNO! The site URL needs to be dynamic.
-
ThN over 5 yearsThis 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.
-
Ray about 5 yearsFor some reason when I did this, it made
$_POST
always empty -
Risteard almost 5 yearsI had a similar problem and I had to apply the rules to the https.conf file. See my answer below.
-
William Entriken over 4 yearsCan also add this line in case you ever run this site locally for testing.
RewriteCond %{REMOTE_ADDR} !^127\.0\.0\.1$
-
Alexufo almost 2 yearswhy you want to delete it?