How to get Elastic Beanstalk nginx-backed proxy server to auto-redirect from HTTP to HTTPS?

Amazon Web-ServicesNginxAmazon Ec2Amazon Elastic-Beanstalk

Amazon Web-Services Problem Overview


I've got a Node.js powered site that I'm running on Amazon Elastic Beanstalk.

My Node.js app listens on port 8080, and I'm using the nginx elastic load balancer configuration with my EB app, listening on port 80 and 443 for HTTP and HTTPS.

However, I only want to accept traffic in my app that has come via HTTPS.

I could rig something up in the app to deal with this, but am interested in a way to get the load balancer to redirect all HTTP requests to my site via HTTPS.

Amazon Web-Services Solutions


Solution 1 - Amazon Web-Services

After several false-starts with ideas from Amazon's paid support, they did come through in the end. The way you get this to work is you configure your environment to respond to both port 80 and 443. Then create a folder in your main Node.js app folder called .ebextensions, and you place a file named 00_nginx_https_rw.config in there, with this text as the contents:

files:
  "/tmp/45_nginx_https_rw.sh":
    owner: root
    group: root
    mode: "000644"
    content: |
      #! /bin/bash
      
      CONFIGURED=`grep -c "return 301 https" /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf`
      
      if [ $CONFIGURED = 0 ]
        then
          sed -i '/listen 8080;/a \    if ($http_x_forwarded_proto = "http") { return 301 https://$host$request_uri; }\n' /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf
          logger -t nginx_rw "https rewrite rules added"
          exit 0
        else
          logger -t nginx_rw "https rewrite rules already set"
          exit 0
      fi

container_commands:
  00_appdeploy_rewrite_hook:
    command: cp -v /tmp/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/appdeploy/enact
  01_configdeploy_rewrite_hook:
    command: cp -v /tmp/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact
  02_rewrite_hook_perms:
    command: chmod 755 /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh
  03_rewrite_hook_ownership:
    command: chown root:users /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh

Amazon's support team explained: This config creates a deployment hook which will add the rewrite rules to /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf.

(Previously they had offered me .config's that copied separate files into /etc/nginx/conf.d, but those either had no effect, or worse, seemed to overwrite or take precedence over the default nginx configuration, for some reason.)

If you ever want to undo this, i.e. to remove the hooks, you need to remove this ebextension and issue a command to remove the files that it creates. You can do this either manually, or via ebextensions commands you put in place temporarily:

/opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh
/opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh

I haven't tried this, but presumably something like this would work to remove them and undo this change:

container_commands:
  00_undochange:
    command: rm /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh
  01_undochange:
    command: rm /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh

Hope this can help someone else in the future.

Solution 2 - Amazon Web-Services

The accepted answer no longer worked for me. The default port was a different one. Also the location of the config file has changed. I am setting up a Ruby On Rails application with Puma.

I talked to the paid support, we figured it out by just running the commands manually on the running instance. Then I was able to figure out the below solution. Just by logging in and restarting nginx things then worked.

files:
  "/tmp/45_nginx_https_rw.sh":
    owner: root
    group: root
    mode: "000644"
    content: |
      #! /bin/bash

      CONFIGURED=`grep -c "return 301 https" /opt/elasticbeanstalk/support/conf/webapp_healthd.conf`

      if [ $CONFIGURED = 0 ]
        then
          sed -i '/listen 80;/a \    if ($http_x_forwarded_proto = "http") { return 301 https://$host$request_uri; }\n' /opt/elasticbeanstalk/support/conf/webapp_healthd.conf
          logger -t nginx_rw "https rewrite rules added"
          exit 0
        else
          logger -t nginx_rw "https rewrite rules already set"
          exit 0
      fi

container_commands:
  00_appdeploy_rewrite_hook:
    command: cp -v /tmp/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/appdeploy/enact
  01_configdeploy_rewrite_hook:
    command: cp -v /tmp/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact
  02_rewrite_hook_perms:
    command: chmod 755 /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh
  03_rewrite_hook_ownership:
    command: chown root:users /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh

Notice how I changed the port number and the location of the config file.

Solution 3 - Amazon Web-Services

You could handle the redirect via your Node.js app.

Amazon sends the X-Forwarded-Proto header which equals http when the client has connected insecurely.

The following middleware should be inserted right after initializing Express and before defining your routes to automatically redirect the client to the corresponding HTTPS endpoint:

// Redirect to HTTPS
app.use(function (req, res, next) {
    // Insecure request?
    if (req.get('x-forwarded-proto') == 'http') {
        // Redirect to https://
        return res.redirect('https://' + req.get('host') + req.url);
    }

    next();
});

Solution 4 - Amazon Web-Services

I was able to get this working with a slightly simpler solution.

Please note, this is an elastic beanstalk deployed SINGLE instance, not load balenced.

This was my ebextension I added.

files:
  "/etc/nginx/conf.d/000_my_config.conf":
    mode: "000755"
    owner: root
    owner: root
    content: |
      server {
          listen 8080;
          return 301 https://$host$request_uri;
      }

Solution 5 - Amazon Web-Services

I am running the 'Ruby2 Puma' environment on AWS Elastic Beanstalk which may have a slightly different configuration than above. In my environment I needed to use 'listen 80' instead of 'listen 8080'.

sslredirect.config based on elloworld111's answer:

files:
  "/etc/nginx/conf.d/000_my_config.conf":
    mode: "000755"
    owner: root
    owner: root
    content: |
      server {
          listen 80;
          return 301 https://$host$request_uri;
      }

Solution 6 - Amazon Web-Services

I'm working with Elastic Beanstalk and Docker, so have taken a slightly different route to get things running for me, but very much inspired by the accepted answer. This script injects required config into /etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf. (If anyone has a more elegant solution would love to see it)

This script also enables the Beanstalk healthcheck to hit my healthcheck endpoint (in my case api/healthcheck) Better to allow the LoadBalancer to hit the app, rather than terminate at Nginx.

files:
  "/tmp/45_nginx_https_rw.sh":
    owner: root
    group: root
    mode: "000755"
    content: |
      #! /bin/bash

      CONFIGURED=`grep -c "return 301 https" /etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf`

      if [ $CONFIGURED = 0 ]
        then
          sed -i "/access.log;/a \ \ \ \ \ \ \ \ location /api/health-check { proxy_pass http://docker; }" /etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf
          sed -i "/proxy_add_x_forwarded_for;/a \ \ \ \ \ \ \ \ \ \ \ \ if (\$http_x_forwarded_proto != 'https') { return 301 https://\$host\$request_uri; }" /etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf
          logger -t nginx_rw "https rewrite rules added"
          exit 0
        else
          logger -t nginx_rw "https rewrite rules already set"
          exit 0
      fi

container_commands:
  00_run_script:
    command: /tmp/45_nginx_https_rw.sh

Solution 7 - Amazon Web-Services

I was able to get this to work in a different way. I changed my load balancer to forward port 80 traffic to port 8082, and changed the firewall rules (inbound on the instance, outbound on the firewall) to allow that. And then added this file in .ebextensions:

files:
  "/etc/nginx/conf.d/50-atd-hotel-http-redirect.conf":
    mode: "000644"
    owner: root
    group: root
    content: |
      server {
        listen   8082;

        return 301 --WHATEVER DESTINATION YOU WANT--;
      }

Solution 8 - Amazon Web-Services

The accepted answer did not work for me. After many tries (and hours of googling), I find something that did work for me. I too have a Node.js powered site that I'm running on Elastic Beanstalk.

I used the script from here : https://adamjstevenson.com/tutorials/2017/02/02/configuring-and-forcing-https-for-aws-elastic-beanstalk.html

The only modification I did was switch out the

/opt/elasticbeanstalk/support/conf/webapp_healthd.conf

by

/etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf

so it gives this :

files:
  "/tmp/45_nginx_https_rw.sh":
    owner: root
    group: root
    mode: "000644"
    content: |
      #! /bin/bash

      CONFIGURED=`grep -c "return 301 https" /etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf`

      if [ $CONFIGURED = 0 ]
        then
          sed -i '/listen 80;/a \    if ($http_x_forwarded_proto = "http") { return 301 https://$host$request_uri; }\n' /etc/nginx/sites-available/elasticbeanstalk-nginx-docker-proxy.conf
          logger -t nginx_rw "https rewrite rules added"
          exit 0
        else
          logger -t nginx_rw "https rewrite rules already set"
          exit 0
      fi

container_commands:
  00_appdeploy_rewrite_hook:
    command: cp -v /tmp/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/appdeploy/enact
  01_configdeploy_rewrite_hook:
    command: cp -v /tmp/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact
  02_rewrite_hook_perms:
    command: chmod 755 /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh
  03_rewrite_hook_ownership:
    command: chown root:users /opt/elasticbeanstalk/hooks/appdeploy/enact/45_nginx_https_rw.sh /opt/elasticbeanstalk/hooks/configdeploy/enact/45_nginx_https_rw.sh

After eb deploy, just restart your nginx sudo service nginx restart and you're set.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMason G. ZhwitiView Question on Stackoverflow
Solution 1 - Amazon Web-ServicesMason G. ZhwitiView Answer on Stackoverflow
Solution 2 - Amazon Web-ServicesHendrikView Answer on Stackoverflow
Solution 3 - Amazon Web-ServicesElad NavaView Answer on Stackoverflow
Solution 4 - Amazon Web-Serviceselloworld111View Answer on Stackoverflow
Solution 5 - Amazon Web-ServicesAdam DView Answer on Stackoverflow
Solution 6 - Amazon Web-ServicesIain HunterView Answer on Stackoverflow
Solution 7 - Amazon Web-ServicesRobOView Answer on Stackoverflow
Solution 8 - Amazon Web-ServicesMatija MarticView Answer on Stackoverflow