23

We've got a shopping site which we're hosting on a shared host (Mediatemple Gridserver). Some parts of the site need to use HTTPS (checkout etc) but the rest should be using HTTP.

Does anyone know how we can always force the correct use of HTTP/HTTPS for particular URLs? We've had it working in various states but we can't get a request for a page that should be on HTTP but is requested with HTTPS to switch back correctly.

I've had a look around SO but couldn't find a suitable answer to this.

Alistair Holt
  • 1,432
  • 3
  • 17
  • 28

6 Answers6

40

I use something similar to this for my admin folder in wordpress:

#redirect all https traffic to http, unless it is pointed at /checkout
RewriteCond %{HTTPS} on
RewriteCond %{REQUEST_URI} !^/checkout/?.*$
RewriteRule ^(.*)$ http://mydomain.com/$1 [R=301,L]

The RewriteCond %{HTTPS} on portion may not work for all web servers. My webhost requires RewriteCond %{HTTP:X-Forwarded-SSL} on, for instance.

If you want to force the reverse, try:

#redirect all http traffic to https, if it is pointed at /checkout
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} ^/checkout/?.*$
RewriteRule ^(.*)$ https://mydomain.com/$1 [R=301,L]

If you want some alternate ways to do it, check out askapache.

Curtis Tasker
  • 11,115
  • 2
  • 23
  • 23
  • I checked the code on my server, using both blocks listed above, and it works fine with no redirect loops. – Curtis Tasker Jul 20 '09 at 19:21
  • +1. Great answer. The `RewriteCond %{HTTP:X-Forwarded-SSL} on` was needed in my case because I am using RackSpace's cloud sites. – Luke Stevenson Aug 15 '11 at 00:19
  • 2
    [@wilmoore's](http://stackoverflow.com/questions/1108706/correctly-switching-between-http-and-https-using-htaccess/#9917579) suggestion to use `%{SERVER_NAME}` addition will be useful – antitoxic May 10 '12 at 10:11
  • 3
    I had to use this check to get it to work `RewriteCond %{HTTP:X-Forwarded-Proto} !https` – gregn3 Feb 10 '16 at 17:09
16

This should work in pretty much every scenario and should work in your actual vhost or .htaccess:

RewriteEngine on
RewriteCond %{SERVER_PORT} ^80$
RewriteRule ^(.*)$ https://%{SERVER_NAME}/%{REQUEST_URI} [R=301,L]

(do not forget the slash before %{REQUEST_URI} as this may allow passing a portnumber, which is dangerous)

Community
  • 1
  • 1
Wil Moore III
  • 6,968
  • 3
  • 36
  • 49
  • 1
    Your answer, while elegant, has a small mistake. The last line should have a slash (/) after SERVER_NAME, or www.test.com/dir forwards to http://www.test.comdir – Juhani Sep 28 '12 at 06:11
  • @Juhani Thanks for your comment. Actually, %1 maps to the {REQUEST_URI} environment variable which is root relative. In other words, if you requested http://example.com/123, then REQUEST_URI === '/123'; thus, another slash would be superfluous. It would still work because in most modern configurations, apache will strip off the extra slash. Unless you like having apache do more work, you don't need the slash. That being said, I did update my original answer to use %{REQUEST_URI} instead of %1 as it is more intention revealing. – Wil Moore III Sep 28 '12 at 16:56
  • @wilmoore: that's correct when the options are placed in the server configuration file, as the rewrite engine can [hook](http://httpd.apache.org/docs/2.4/rewrite/tech.html) URL-to-filename-translation. However, when placed in a [per-directory](http://httpd.apache.org/docs/2.4/rewrite/intro.html#htaccess) configuration file, the URI has already been mapped to a pathname by the time the rewrite engine steps in. The rewrite engine handles this by stripping the directory prefix, which ends up removing the leading '/', and matching against the result. – outis Feb 07 '13 at 03:15
  • @wilmoore: it was actually fine without the "/" before the "%{REQUEST_URI}", which holds the original request URI and isn't updated by the rewrite engine. I was trying to point out that "$1" wouldn't be correct in a per-directory context for the reason stated by Juhani. Thus your first change didn't just make the intent clearer, it corrected the error Juhani mentioned. Without "%{REQUEST_URI}", you'd have to use `RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]`. Sorry my comment caused some confusion. – outis Feb 07 '13 at 09:18
  • @outis, thanks...I haven't had to deal with an Apache server for quite a while so I wasn't able to test. Thanks for the reassurance. – Wil Moore III Feb 08 '13 at 05:24
  • Probably better/safer to use "RewriteCond %{HTTPS} off" than "RewriteCond %{SERVER_PORT} ^80$"....(as in the other answer) – Malachi Feb 21 '17 at 21:09
  • @Malachi You are probably correct; however, I can't verify it at this time. – Wil Moore III Feb 22 '17 at 16:48
6
RewriteEngine on
RewriteCond %{HTTPS} off [OR] 
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://%{SERVER_NAME}/%{REQUEST_URI} [R=301,L]

I had some problem being behind a loadballancer. This how i fixed it.

Sjaak Wish
  • 455
  • 5
  • 8
  • 2
    Does that actually work? It threw me into a redirect loop. If you have loadbalancer, the HTTPS will not be set and the first RewriteCond will be met. If you don't, the second RewriteCond will be met. Thus every time you will get redirected again. I got it working using (another answer)[http://stackoverflow.com/a/26623196/2182900] that is basically the same thing but without the `[OR]` - you only redirect if both HTTPS is off and the HTTP:X-Forwarded-Proto is not https. – Džuris Apr 04 '17 at 10:05
2

For me worked this (I used it for wordpress site and redirecting to HTTPS). You have to add the condition and rule lines just behind RewriteEngine and RewriteBase lines:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

# I added these two lines for redirect to HTTPS
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^(.*)$ https://www.yoursite.com/$1 [R=301,L]
# (end of custom modifications)

RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress`

Have a look to condition RewriteCond %{HTTP:X-Forwarded-Proto} !https - only this worked for my server hosting. (I tried RewriteCond %{SERVER_PORT} !^443$ or RewriteCond %{HTTPS} off as well, but without success.

David Najman
  • 487
  • 4
  • 7
1

As detailed in this answer, fix your application to use https:// links when needed. Don't rely on automatic redirections, this could lead you to a false sense of security if you haven't made your links/forms served over https:// go to https:// URLs too. Using mod_rewrite automatically makes it harder to detect such mistakes (which can also be vulnerabilities).

Community
  • 1
  • 1
Bruno
  • 119,590
  • 31
  • 270
  • 376
0

I think it should be:

RewriteCond %{HTTPS}  =on
^/checkout(.*) http://shoppingsite.com/checkout$1 [R]

See the mod_rewrite documentation.

Matthew Flaschen
  • 278,309
  • 50
  • 514
  • 539