Skip to main

Setup nginx as Reverse Proxy for Node.js app under a subpath

3 min read

nginx is a simple yet powerful HTTP server, Reverse proxy, Load Balancer, etc. My favorite part about nginx is how easy it is to setup and configure with its simple nginx.conf file. I haven’t worked on backend services much but I wanted to play around with nginx for a side project and needed to serve static assets (html, css, js etc) from a directory for requests on / route as well as forward requests on /api/* route to an api service (Node.js).

http://mydomain.tld/styles.css 👉 /  # Serve static files from a directory
http://mydomain.tld/api/users 👉 /api/*  # Proxy requests to Node.js service as /*

Here’s the nginx.conf from (/etc/nginx/nginx.conf) that serves static assets. The static asset files are placed in /usr/share/nginx/html directory.

user nginx;
worker_processes auto;

events {
  worker_connections 1024;
}

http {
    server {
        listen 3333;
        listen [::]:3333;

        location / {
          root /usr/share/nginx/html;
          sendfile on;
          tcp_nopush on;
          tcp_nodelay on;
          keepalive_timeout 65;
        }
    }
}

I have a simple Node.js API service running on 8080 port.

const fastify = require("fastify")({ logger: true });

fastify.get("/", async (request, reply) => {
  return { hello: "world" };
});

const start = async () => {
  try {
    await fastify.listen({ port: 8080 });
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};
start();

We can use location {} to handle incoming requests to /api.

location ^~ /api {
  # value can be anything that you want to be proxied
  proxy_pass http://localhost:8080/;
}

^~ ensures requests starting with /api are handled in this location block and a trailing slash / to the proxied path ensures requests are forwarded under / path to our Node.js service (http://localhost:8080/👈).

i.e Request /api/xyz/ is forwarded as /xyz to Node.js service.

Note: You can ignore adding trailing slash if you are handling all requests as is inside Node.js service under /api/* path. For example:

fastify.get("/api/*", () => {});

Let’s test both the services with a simple curl -v command

Static HTML file served from nginx Response from API

Since we are proxying Node.js service here, Node.js sees all requests as though they were coming from nginx. We can also add additional proxy headers in nginx if we want them to be forwarded to our proxied node.js server using proxy_set_header.

location ^~ /api {
  proxy_pass http://localhost:8080/;
  proxy_set_header Host $http_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;
}

Complete nginx.conf config:

user nginx;
worker_processes auto;

events {
  worker_connections  1024;
}

http {
    server {
        listen 3333;
        listen [::]:3333;

        location / {
          root /usr/share/nginx/html;
          sendfile  on;
          tcp_nopush on;
          tcp_nodelay on;
          keepalive_timeout 65;
        }

        location ^~ /api {
          proxy_pass http://localhost:8080/;
          proxy_set_header Host $http_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;
        }
    }
}

Further Reading / References: