Home > Enterprise >  nginx reverse proxy works with port example.com:3001 but not without it
nginx reverse proxy works with port example.com:3001 but not without it

Time:11-14

I am running a NestJS application via PM2 on port 3001 in an AWS EC2 instance.

I configured SSL using certbot / Let's Encrypt and nginx. I want the NestJS application to serve as my API server hence the *.api.example.com. I have the client assets (HTML, JavaScript, and CSS) in S3 and a CloudFront distribution.

The issue I am running into is as follows:

  • If I navigate to staging.api.example.com in the browser, I receive a 502 Bad Gateway
  • If I navigate to staging.api.example.com:3001 in the browser I receive a 404
  • If I navigate to staging.api.example.com:3001/users which is a valid API route, everything works fine.

I want requests from staging.api.example.com to hit my NestJS server running at http://127.0.0.1:3001 in the EC2 instance via my nginx reverse proxy configuration.

I also cannot figure out why I have to include the port in the URL in order to reach my backend.

In my EC2 instance I had to add a custom rule to allow TCP traffic on port 3001 which doesn't seem right to me. I'm using a VPC, so I'm not sure if that's part of the problem.

IP Version Type Protocol Port Range
IPv4 Custom TCP TCP 3001
IPv4 HTTP TCP 80
IPv4 HTTPS TCP 443

Steps I took install certbot and generate a certificate for staging.api.example.com

sudo yum update -y
sudo amazon-linux-extras install nginx1
sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo yum-config-manager --enable epel
sudo yum install certbot python2-certbot-nginx -y
sudo certbot --nginx

How I start the server in my EC2 instance

pm2 start dist/src/main.js --name example

NestJS application configuration

  const app = await NestFactory.create(AppModule, {
    cors: true,
    httpsOptions: {
      key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
      cert: fs.readFileSync('/etc/letsencrypt/live/example.com/cert.pem')
    }
  });
  await app.listen(3001);

NGINX configuration

server {
    listen     443 ssl; # managed by Certbot
    listen  [::]:443 ssl;

    server_name  staging.api.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location / {
        proxy_connect_timeout 300;
        proxy_read_timeout 300;
        proxy_set_header Host $host:$server_port;
        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_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_cache_bypass $http_upgrade;
        proxy_redirect http:// https://;
    }
}

server {
    if ($host = staging.api.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    listen [::]:80;
    server_name staging.api.example.com;
    return 404; # managed by Certbot
}

NGINX error log

$ sudo tail -f /var/log/nginx/error.log
2022/11/11 20:17:35 [error] 30033#30033: *1 upstream prematurely closed connection while reading response header from upstream, client: ip, server: staging.api.example.com, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:3001/", host: "staging.api.example.com"

NGINX access log

sudo tail -f /var/log/nginx/access.log

# navigate to staging.api.example.com in browser
[11/Nov/2022:20:22:16  0000] "GET / HTTP/1.1" 502 559 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" "-"

# navigate to staging.api.example.com/ in browser
[11/Nov/2022:20:22:22  0000] "GET / HTTP/1.1" 502 559 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36" "-"

CodePudding user response:

It turns out all I needed to do was change the proxy_pass url in the location block to include https

Original NGINX config - does not work

location / {
    ...
    proxy_pass http://127.0.0.1:3001;
    ...
}

Updated NGINX config - works

location / {
    ...
    proxy_pass https://127.0.0.1:3001;
    ...
}
  • Related