4

I think that I have this sorted (thanks mainly to the question How to redirect non-www to www without hardcoding using .htaccess?), but I still don't entirely understand a couple of things.

I would like to force all non-SSL connections to my server to be routed to SSL. I have only one vhost (and that will reliably remain the case for the lifetime of the server), but I would like to avoid hard-coding the domain name, partly so that the httpd.conf files for Staging and Production remain identical.

I know I can force requests to use SSL with a mod_rewrite rule like

RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

or

RewriteCond %{SERVER_PORT} ^80$
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

I have two relatively simple questions, though:

  1. Should I prefer one of those RewriteCond statements over the other for any reason? Presumably if I use the %{SERVER_PORT} variable, any connections on, say, port 8000 would continue to be served in the clear? Is there some reason I should avoid using the %{HTTPS} variable that I'm overlooking?
  2. Will that %{HTTP_HOST} variable in the RewriteRule statement be respected and automagically substitute in whatever the Host: header was from the request? Is there some circumstance under which this might not work?

In case it makes a difference, we're running Apache 2 on RedHat with mod_ssl and the site used Drupal 7.

Sorry for what is a relatively stupid question; Apache sysadmin is by no means a core part of my job, so I'm trying to muddle through as best I can. Thanks, all!

Owen Blacker
  • 631
  • 1
  • 7
  • 20
  • @jaymzcampbell on Twitter pointed out that he'd always use `%{HTTPS}`, are there's no guarantee that your non-ssl is being served over 80, which confirms my suspicion. He also mentioned "afaicr `http_host` comes from the inbound request indeed": http://twitter.com/jaymzcampbell/statuses/158980755693912064 – Owen Blacker Jan 16 '12 at 20:50
  • 2
    There is a possibility for %{HTTP_HOST} not to be set — where it’s the default virtual host on the port and somebody sends a request without including a `Host:` request header. This would be rather rare, though, to say the least. Beyond that, I can’t think of a scenario where it would occur. – Mo. Jan 17 '12 at 13:38
  • Ah, I would guess that's plausible here — there is only the one virtual host on this server and it's possible-but-unlikely that someone might try to connect with `HTTP/1.0`, not sending a `Host:` header. Should I worry about it, in your opinion? – Owen Blacker Jan 17 '12 at 13:53
  • I wonder if there's a way to detect the HTTP Version in apache, and redirect them to somewhere that says "Upgrade your client, bitches" – Tom O'Connor Jan 17 '12 at 13:54
  • 1
    There is — create a default (i.e., first in the list on that port) virtual host which does just that (it doesn’t even have to say “upgrade your client” — “you didn’t specify a virtual host in your request, so we can’t process further” will do). – Mo. Jan 17 '12 at 13:59
  • Given this is a client site, I'd rather not be quite so aggressive towards users with crappy old user agents. There might be a reason for it, after all ;o) I could live with sending them a `406` response, though. – Owen Blacker Jan 17 '12 at 14:05
  • @OwenBlacker Yes, but you get the drift. – Tom O'Connor Jan 17 '12 at 14:28
  • Does IE6 support HTTP1.1? I'd have thought that would be the biggest source of old user agents. – Tom O'Connor Jan 17 '12 at 14:29
  • @TomO'Connor: Indeed. And it does support HTTP/1.1 but as an option, so I can't rely on it. Gah! not sure our statement of work supports IE6, though… ;o) – Owen Blacker Jan 17 '12 at 15:40

1 Answers1

7

I've always used something like the following:

RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L]

Because occasionally, I like to serve HTTP over ports other than 80. Well, I don't like to do it, but sometimes needs must, etc. %{HTTPS} will be true, for example, if SSL is being used over port 80.

I think I usually use

RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

%{HTTP_HOST} will always be whatever is set as the Host: header by the client.

It occurs to me, however.. that there's another way to do this.

<VirtualHost *:80>
   ServerName mysite.example.com
   Redirect permanent / https://mysite.example.com/
</VirtualHost>

You'll notice there's no DocumentRoot in the above block. If you're redirecting everything, you don't need one.

If you only wanted to redirect a bit of your site to SSL, you could just do

Redirect permanent /secure https://mysite.example.com/secure

I think the Redirect option is more preferable for full site HTTPS forcing, because it's one less level of insanity (as provided so kindly by mod_rewrite).

It might even be faster, as there'd be one less module to load / run.

Tom O'Connor
  • 27,440
  • 10
  • 72
  • 148
  • 2
    http://wiki.apache.org/httpd/RedirectSSL This is pretty much what Apache themselves say. – Tom O'Connor Jan 17 '12 at 13:35
  • We need to use `mod_rewrite` elsewhere in the configuration, so I think I'd rather use your first suggestion and avoid having to specify `ServerName`, but thanks, that's exactly what I needed to know. – Owen Blacker Jan 17 '12 at 13:37
  • 1
    Just implemented this and it seems to be working a treat. Thank you very much Tom and Mo. – Owen Blacker Jan 18 '12 at 13:41
  • @TomO'Connor doest RedirectSSL work with different hostname? – Tobia Apr 06 '22 at 08:13