1

Most people type in the browser bar: mysite.com and not https://mysite.com.

Alot of developers including myself have something like this in their Nginx config file which means that this mysite.com request causes a redirect to the https:// site:

server {
    listen 80;
    server_name mysite.com;
    return 301 https://$server_name$request_uri;

}

Google pagespeed team recently said [ref] these redirects are terrible for performance particularly on mobile because the redirect causes the request to go back through the mobile carrier network.

My question is is there any other way to write the nginx.conf such that people typing in the server_name don't experience this http:// to https:// redirect penalty?

tim peterson
  • 683
  • 2
  • 9
  • 18

2 Answers2

4

No, this would require that you change the behavior of the browser. It's all request response based. The user types example.com in his browser bar and the browser automatically adds http:// in front of that. So your server will always get the first request on http://example.com and you can only answer with a redirect to your SSL enabled address if there is nothing without SSL.

Denying the request, as proposed by Nathan is absolutely no option. Because the browser will display an error page that this website is not reachable and might not even exist.

But there is something else you can do: HTTP Strict Transport Security (HSTS)

HSTS tells the browser that your site is only reachable via SSL and that subsequent requests should always be auto-completed with https:// instead of http://. You can achieve this in nginx with the following lines in your SSL server block:

add_header Strict-Transport-Security "max-age=262974383";

http {
  # One server listening on port 80 and sending the redirect to HTTPS
  server {
    server_name example.com;
    return 301 https://$server_name$request_uri;
  }

  # Our actual server handling incoming requests.
  server {
    listen 443 ssl;
    server_name example.com;
    ssl_certificate      /etc/ssl/my_site.pem;
    ssl_certificate_key  /etc/ssl/my_site.key;
    # Tell the browser that he should always visit us with SSL.
    add_header Strict-Transport-Security "max-age=262974383";
  }
}
Fleshgrinder
  • 3,638
  • 2
  • 16
  • 19
  • thanks, this is more what I was looking for, trying it out now to see if it does what i need – tim peterson Jul 22 '13 at 19:08
  • hmm i don't have a ssl.conf file, can I put this in my nginx.conf file? If so, in what block? If not, how would I make my server recognize this newly created ssl.conf file? – tim peterson Jul 22 '13 at 19:23
  • Put it inside your SSL server block where you have the `listen`, `server_name` and `ssl_certificate*` lines. My configuration is very advanced, I just included for people who'd like to see it. – Fleshgrinder Jul 22 '13 at 19:32
  • hmm, if I don't have a `listen 80` server block then the first page load of `mysite.com` fails and with the second load then it accepts it. I only have a `listen 443` server block containing your `Strict-Transport-Security` directive. – tim peterson Jul 22 '13 at 20:18
  • here's a gist of my nginx.conf https://gist.github.com/tim-peterson/6057327 – tim peterson Jul 22 '13 at 20:28
  • Of course, you need a server listening on port 80. HSTS is not supported by all clients and you want to redirect them and new users (who don't know that your site is only reachable via SSL yet) to the SSL enabled site. See my edited answer. – Fleshgrinder Jul 22 '13 at 21:35
  • Thanks, your added code above is exactly what i'm now doing. Try this on https://developers.google.com/speed/pagespeed/insights. I'm still getting a `Avoid landing page redirects` warning when using `"mysite.com" but not "https://mysite.com"` despite using the HSTS directive. So I guess I'm wondering how I know if the directive is actually doing anything? Also, what happens when the `max-age` expires? – tim peterson Jul 22 '13 at 22:21
  • Yes, and it will report it in the future as well, because of the browser behavior I told you about. You can't solve this issue unless you change the browser behavior (which we can't do, because we're all only working in a sandbox). It would be great if browser would automatically check for `https`, but that's the reason why there are plug-ins like "SSL everywhere" for Firefox or Chrome. Go to https://www.ssllabs.com/ssltest/analyze.html to check that HSTS is working correctly and/or check your HTTP headers with the dev console of your browser. If the `max-age` expires, the browser falls ... – Fleshgrinder Jul 23 '13 at 09:12
  • ... back to its default behavior and picks up the HSTS header again until `max-age` is reached. But the browser will update the `max-age` on each visit (or at least it should). So a user that is visiting your page on a regular basis will never have this problem. – Fleshgrinder Jul 23 '13 at 09:13
1

Either don't listen on port 80 (which will toss an error), or deny the request:

server {
    listen 80;
    server_name mysite.com;
   location / {
  deny    all;
}
}

Then just have your normal 443 block.

Nathan C
  • 14,901
  • 4
  • 42
  • 62
  • I assume `deny all` still gives you a blank screen even if there is no error? I guess my question is is there any way to still show the site without a redirect? – tim peterson Jul 22 '13 at 16:03
  • If you don't absolutely require https for everything, then use http for your main page and https everywhere else. It highly depends on your application though. – Nathan C Jul 22 '13 at 16:10