Redirect all http requests behind Amazon ELB to https without using if

32,420

Solution 1

If it's working correctly like that, don't be scared of it. http://wiki.nginx.org/IfIsEvil

It is important to note that the behaviour of if is not inconsistent, given two identical requests it will not randomly fail on one and work on the other, with proper testing and understanding ifs can be used. The advice to use other directives where available still very much apply, though.

Solution 2

  1. Setup your AWS ELB mapping ELB:80 to instance:80 and ELB:443 to instance:1443.
  2. Bind nginx to listen on port 80 and 1443.
  3. Forward requests arriving at port 80 to port 443.
  4. Health check should be HTTP:1443. It rejects the HTTP:80 because the 301 redirect.

aws elb setup

NGINX Setup

    server {
       listen         80;
       server_name    www.example.org;
       rewrite        ^ https://$server_name$request_uri? permanent;
    }

    server {
       listen         1443;
       server_name    www.example.org;
   } 

Solution 3

This solution uses conditional logic, but as the accepted answer suggests, I also think this is ok. Ref: https://stackoverflow.com/questions/4833238/nginx-conf-redirect-multiple-conditions

Also, this doesn't require opening any additional ports in the aws security settings for the image. You can terminate ssl in the AWS LB, and route https traffic to http port 80 on your instance.

In this example the LB health check hits /health on port 80 which routes to the app server, so the health check validates both nginx and your app are breathing.

server {
  listen 80 default deferred;

  set $redirect_to_https 0;
  if ($http_x_forwarded_proto != 'https') {
    set $redirect_to_https 1;
  }
  if ($request_uri = '/health') {
    set $redirect_to_https 0;
  }
  if ($redirect_to_https = 1) {
    rewrite ^ https://www.example.com$request_uri? permanent;
  }
  ...
}
Share:
32,420

Related videos on Youtube

Jordan Reiter
Author by

Jordan Reiter

I'm mostly a code monkey but interested in how information is used generally, even wrote my master's thesis on it, sort of. Currently working on developing an online community for academics as well as a digital library for educational professionals.

Updated on September 18, 2022

Comments

  • Jordan Reiter
    Jordan Reiter over 1 year

    Currently I have an ELB serving both http://www.example.org and https://www.example.org.

    I would like to set it up so any request pointing to http://www.example.org is redirect to https://www.example.org.

    The ELB sends the https requests as http requests, so using:

    server {
          listen         80;
           server_name    www.example.org;
           rewrite        ^ https://$server_name$request_uri? permanent;
    }
    

    will not work because requests made to https://www.example.org will still be made to port 80 on nginx.

    I know it's possible to rewrite it as

    server {
          listen         80;
          server_name    www.example.org;
          if ($http_x_forwarded_proto != "https") {
              rewrite ^(.*)$ https://$server_name$1 permanent;
          }
    }
    

    But everything I've read said that if should be avoided at all costs within nginx configuration, and this would be for every single request. Also, it means I have to set up a special separate configuration for the health check (as described here: "…when you are behind an ELB, where the ELB is acting as the HTTPS endpoint and only sending HTTP traffic to your server, you break the ability to respond with an HTTP 200 OK response for the health check that the ELB needs").

    I'm considering putting the login in the code of the web application rather than the nginx configuration (and for the purposes of this question let's assume it's a Django-based application), but I'm not certain whether that would be more overhead than the if in configuration.

    • YuAn Shaolin Maculelê Lai
      YuAn Shaolin Maculelê Lai almost 7 years
      Hi can you please tell me where do you put these code?
    • Jordan Reiter
      Jordan Reiter almost 7 years
      @YuAnShaolinMaculelêLai Sure. These are configuration files for nginx, so I just put the code in a file in /etc/nginx/conf.d/. I usually name the file domainname.conf where "domainname" is the domain of the website in question. You can name the file whatever you want so long as it ends with .conf.
    • YuAn Shaolin Maculelê Lai
      YuAn Shaolin Maculelê Lai almost 7 years
      Thank you very much. I tried to create a new file following by .conf. But it didn't work for me. Then I put the code in the file that generated from AWS in /etc/nginx/conf.d/. It works now.
  • samkhan13
    samkhan13 about 9 years
    what about the health check settings? could you please elaborate on that as well.
  • samkhan13
    samkhan13 about 9 years
    this did not work with the helth check set to http:80, the health check fails.
  • cbron
    cbron almost 9 years
    Health check should be HTTP:1443. It rejects the HTTP:80 because the 301 redirect.
  • Ron
    Ron over 7 years
    this mostly worked for me but the rewrite line did not work with my wildcard domain. This other post fixed that part: serverfault.com/questions/447258/…
  • Edward
    Edward over 6 years
    there must be a more elegant way of doing this
  • Excalibur
    Excalibur about 6 years
    This page also says that "if has problems when used in location context". It seems to me that you can do what you're needing to do outside of location {}, in server {} instead. (But please let me know if this is incorrect!)
  • Aryeh Leib Taurog
    Aryeh Leib Taurog over 5 years
    unfortunately, it isn’t yet supported in Cloudformation