Search code examples
node.jsamazon-web-servicesexpressnginxamazon-elastic-beanstalk

Node Not Registering HTTPS from Nginx Reverse Proxy


I've been struggling to send a secure session cookie with express because node isn't recognizing that the request is https. I have an nginx reverse proxy in a docker-compose app. I deploy to Elastic Beanstalk on AWS which handles SSL. I've added logs to check that X-Forwarded-Proto is https in my production environment (at the nginx proxy). But when I log in node

  req.headers["x-forwarded-proto"],
  req.protocol,

It shows as http http. I am confused on how this setup is supposed to be done.

http {
    log_format proxy_debug '$remote_addr - $remote_user [$time_local] "$request" '
                       '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                       '"X-Forwarded-Proto: $http_x_forwarded_proto"';
    # Set the access log file and apply the custom format
    access_log /var/log/nginx/proxy_debug.log proxy_debug;


    upstream client {
        server client:3000;
    }

    upstream api {
        server api:8080;
    }

    server {
        listen 80;

        location /api {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Host $host;

            # Use the custom log format
            access_log /var/log/nginx/proxy_debug.log proxy_debug;
            
            rewrite /api/(.*) /$1 break;
            proxy_pass http://api;

        }

        location / {
            proxy_pass http://client;

            # Enable WebSocket support, needed for HMR in React
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
        }
    }
}

events {}

I've tried setting up a SSL certificate on the reverse proxy itself and using https on my dev environment to debug unsuccessfully.

I see posts like this Configuring HTTPS for Express and Nginx and feel like there should be a simple solution.


Solution

  • In nginx, you are overwriting the X-Forwarded-Proto header from the load balancer with this line:

    proxy_set_header X-Forwarded-Proto $scheme;
    

    The value of X-Forwarded-Proto might be HTTPS from the load balancer, but you are then changing it to HTTP (the value of $scheme) when you send the request to Express. So from Express's point of view, the request is not secure.

    What you need, is to have Nginx simply take the value of X-Forwarded-Proto that it receives from the load balancer, and pass it on to Express, unchanged. To do this, you can change that line in your config to the following:

    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    

    Note that the $http_x_forwarded_proto is available by default in Nginx. You don't need to do anything to declare that variable in your config. This is documented here

    $http_name

    arbitrary request header field; the last part of a variable name is the field name converted to lower case with dashes replaced by underscores