October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk11 min

How to Point a Subdomain to a Node.js App on Your VPS (DNS, NGINX, and HTTPS)

Connect a subdomain to a Node.js app by configuring DNS for the VPS, proxying ports 80 and 443 through NGINX to a local Node.js port, issuing TLS, and checking each layer when routing fails.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Pointing api.example.com at a Node.js application requires two separate configurations: DNS sends the hostname to your VPS, and a reverse proxy such as NGINX forwards web requests from ports 80 and 443 to the Node.js process on its private port, such as 127.0.0.1:3000. DNS records do not contain port numbers, so creating an A, AAAA, or CNAME record alone cannot make a public request reach port 3000.

How the connection works

A browser first resolves the subdomain through DNS. The resulting IPv4 address, IPv6 address, or canonical hostname identifies the VPS, but it does not identify the application port. The browser then makes an HTTP or HTTPS request to the standard web ports: 80 for HTTP or 443 for HTTPS.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

NGINX listens on those public ports, selects the virtual server whose server_name matches the requested hostname, and proxies the request to the Node.js service listening locally. Keeping Node.js on a loopback address prevents direct public access to its application port while still allowing NGINX to reach it.

There are therefore four independent pieces to verify:

  • The subdomain’s DNS record points to the intended VPS.
  • The Node.js process is running and listening on the expected local address and port.
  • NGINX has an enabled server block for the exact hostname and can connect to that local port.
  • The TLS certificate and HTTP-to-HTTPS behavior are configured after DNS is publicly resolvable.

Choose the correct DNS record

Use an A record for IPv4

Create an A record when the VPS has a stable public IPv4 address. Set the record name to the subdomain label, such as api, and set its value to the VPS’s public IPv4 address. The resulting hostname is api.example.com.

Use an AAAA record for IPv6

Create an AAAA record when the VPS has a configured public IPv6 address. IPv6 clients can then resolve the subdomain directly over IPv6. Confirm that the server, operating-system firewall, and VPS network rules all allow web traffic over IPv6; publishing an AAAA record without working IPv6 connectivity can make some visitors fail even when IPv4 works.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a CNAME for a stable hostname

Use a CNAME when your infrastructure provider gives you a stable hostname rather than an address, or when you want the subdomain to follow another hostname. Set the name to api and the target to the provider’s hostname. A CNAME is an alias, not a port mapping, and it should not be used at the DNS zone apex in systems that do not support apex aliasing.

Record Value to enter When it fits
A Public IPv4 address The VPS has a stable IPv4 address.
AAAA Public IPv6 address The VPS has working, reachable IPv6.
CNAME Provider hostname The provider supplies a hostname or you need an alias.

Do not add :3000 to a DNS value. DNS communicates where an application can be reached; it does not encode the TCP port. Secure transport is a separate SSL/TLS layer.

Prepare the Node.js application

Bind to a private interface

Configure the application to listen on the local interface and the port that NGINX will proxy to. A typical production binding is 127.0.0.1:3000. Use the port your application actually binds; the number is an example, not a requirement.

const port = process.env.PORT || 3000;
app.listen(port, '127.0.0.1', () => {
  console.log(`Listening on ${port}`);
});

If your framework or startup command uses a different binding, make the NGINX proxy_pass destination match it exactly. A process bound only to an unexpected address, or a process that has stopped, produces a gateway error even when DNS is correct.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the process supervised

Run the Node.js service under the process manager used by your VPS deployment. A supervisor should restart the process after a crash and start it again after a reboot. The exact command depends on the distribution and deployment choice, so confirm the service name and startup configuration for your environment before enabling it.

Check the local endpoint first

Before changing NGINX, test the application from the VPS itself:

curl -i http://127.0.0.1:3000/

You should receive the application’s expected status and response. If this request fails, fix the Node.js service, its environment variables, its bind address, or its port before investigating DNS.

Create the NGINX server block

Put a server block in the configuration directory used by your distribution. Debian-based systems commonly separate available and enabled configurations; other distributions may use a single directory. Follow the layout already used by the installed NGINX package.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Minimal HTTP reverse proxy

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

The server_name value must exactly match the hostname clients request. The proxy_pass line identifies the local Node.js endpoint. The forwarded headers preserve the original host, client address chain, and connection scheme so frameworks can generate correct URLs and log meaningful request information.

Use an upstream group for more than one process

A single application can be proxied directly as above. If you later run multiple Node.js instances, define a named upstream group and point proxy_pass at that group:

upstream node_backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://node_backend;
        proxy_http_version 1.1;
        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;
    }
}

An upstream group lists backend servers; NGINX refers to that group through the proxy_pass directive. Add multiple backends only when your application and session handling are designed for it.

Enable the site and reload safely

  1. Save the server block in the distribution’s NGINX configuration location.
  2. Enable it using the mechanism provided by that distribution, such as a symbolic link from an available-sites directory to an enabled-sites directory.
  3. Run the installed NGINX syntax test before touching the running service. The usual command is nginx -t; use the package’s documented equivalent if the binary is elsewhere.
  4. Reload NGINX only after the test reports a valid configuration. A typical systemd command is sudo systemctl reload nginx.
  5. Inspect the service status and error log if the reload fails.

A syntax test catches malformed directives, missing semicolons, and certificate paths that do not exist. It does not prove that the Node.js process is reachable, so retain the local curl check as a separate test.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify DNS before requesting HTTPS

Query the authoritative answer for the exact subdomain and confirm that it returns the VPS address or intended CNAME target. Use a DNS query tool available on your workstation or server, for example:

dig +short api.example.com A
dig +short api.example.com AAAA
dig +short api.example.com CNAME

Only the record types you configured should return a meaningful answer. An unexpected old address usually means a stale record, a second conflicting record, or cached DNS data. Correct the zone first and allow the change to propagate according to the DNS provider’s caching behavior.

Also confirm that the VPS firewall and any provider-level firewall allow inbound TCP traffic on ports 80 and 443. The Node.js port normally does not need to be publicly open when the process listens on loopback.

Issue a TLS certificate

Request the certificate only after the subdomain resolves publicly and NGINX can answer for it. The certificate client and operating system determine the exact command and the paths it installs, so use the paths reported by that client rather than copying a path blindly.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

HTTP-01 validation

With HTTP-01, an ACME client publishes a token at a URL shaped like http://api.example.com/.well-known/acme-challenge/<TOKEN>. The certificate authority retrieves that URL over the public internet. Port 80 must reach the correct server, and an access rule or proxy configuration must not block the challenge.

DNS-01 validation

With DNS-01, you prove control by publishing a TXT record beneath _acme-challenge. This method supports wildcard certificates, but the DNS API credentials used to create or update TXT records require careful protection. Propagation can take as much as an hour depending on the DNS provider, so a failed immediate check does not necessarily mean the TXT value is wrong.

Method Proof location Important requirement Wildcard support
HTTP-01 Public HTTP challenge URL Port 80 and the challenge path must be reachable. Not supported.
DNS-01 TXT record at _acme-challenge Correct TXT value and enough DNS propagation time. Supported.

Split HTTP and HTTPS correctly

After the certificate is installed, use one port-80 server block to redirect and one port-443 server block to terminate TLS and proxy to Node.js:

server {
    listen 80;
    server_name api.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
    }
}

The paths shown are a common ACME layout, not a universal location. Replace them with the certificate and key paths reported by your client. Run the NGINX syntax test again, reload, and request both URLs:

curl -I http://api.example.com/
curl -I https://api.example.com/

The HTTP request should return a permanent redirect to the HTTPS hostname. The HTTPS request should present a certificate valid for the requested hostname and return the Node.js application’s response.

Support WebSockets explicitly

Ordinary HTTP proxying does not automatically complete a WebSocket upgrade. For a WebSocket endpoint, add the HTTP/1.1 and upgrade headers in the relevant location:

location /socket/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    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;
}

Use the actual path used by your application. If only one location needs WebSockets, keep the upgrade directives there instead of applying them indiscriminately to every request.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test the hostname instead of the default server

When several NGINX sites share one VPS, testing the IP address alone may select the default virtual server. Test with the hostname so NGINX must match the intended server_name:

curl -i -H 'Host: api.example.com' http://127.0.0.1/
curl -i http://api.example.com/
curl -i https://api.example.com/

The first command checks virtual-host selection locally. The second checks DNS, network access, and HTTP routing. The third checks the certificate, HTTPS server block, and proxy path. Compare response headers and body with the expected application response, not merely a successful TCP connection.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot by layer

NXDOMAIN or an old address

Inspect the A, AAAA, or CNAME record for spelling, target, and accidental duplicate entries. Remove or correct the wrong record, then wait for cached answers to expire. If an AAAA record exists but IPv6 is not actually configured, remove it or complete the IPv6 setup so clients are not directed to an unreachable address.

The NGINX welcome or default page appears

This proves that the request reached NGINX, not that it reached your application. The matching server block may be absent, disabled, misspelled, or shadowed by another block. Check the enabled configuration, verify the exact server_name, test the configuration, reload, and repeat the hostname-based curl request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

NGINX returns 502 Bad Gateway

NGINX cannot reach the configured backend. Confirm that the Node.js process is running, that it listens on the same address and port used by proxy_pass, and that local firewall rules do not block that connection. Run curl http://127.0.0.1:3000/ from the VPS and inspect the NGINX error log for the precise connection failure.

Certificate validation fails

For HTTP-01, verify that the public challenge URL reaches the correct server without a redirect or access rule that blocks the ACME client. For DNS-01, inspect the TXT record at _acme-challenge.api.example.com and allow time for propagation; the certificate authority may see a different answer until caches update.

WebSockets disconnect immediately

Check that the WebSocket location uses HTTP/1.1 and sends the Upgrade and Connection headers. Confirm that the application path is correct and that the Node.js server itself accepts the upgrade. Ordinary API requests can work while WebSocket upgrades fail, so test the upgrade path separately.

HTTPS loops or reports the wrong scheme

Ensure the HTTPS server block sets X-Forwarded-Proto $scheme and that the application trusts the proxy according to its framework’s documented settings. Keep the redirect only in the port-80 block; placing it in the HTTPS block creates a loop.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Operational checks after deployment

  • Query DNS for every record type you intentionally publish and confirm the answer is the current VPS address or target.
  • Confirm the supervised Node.js service restarts correctly after a controlled restart and remains bound to the expected local port.
  • Run the NGINX configuration test before every reload.
  • Check the HTTP redirect and the final HTTPS certificate from outside the VPS network.
  • Renew the certificate through the ACME client’s renewal mechanism and test renewal before the certificate’s expiry becomes urgent.
  • Review NGINX access and error logs when a request reaches the proxy but the application response is unexpected.

What changes when you add more applications

You can host several Node.js applications on one VPS by giving each application a distinct subdomain, local port, and NGINX server block. For example, api.example.com can proxy to 127.0.0.1:3000 while admin.example.com proxies to 127.0.0.1:3001. DNS records identify the same VPS, and NGINX uses each request’s hostname to select the correct backend.

If you need multiple instances of one application, use an upstream group and verify that sessions, uploads, background jobs, and shared state work across instances. A second backend does not by itself make an application horizontally scalable; it changes how NGINX distributes requests.

Decision guide: which layer should you change?

Observed result Most likely layer First check
NXDOMAIN DNS Record name and zone delegation.
Address resolves to the wrong server DNS or cache A, AAAA, and CNAME targets and propagation.
Default NGINX page Virtual-host selection Enabled server block and exact server_name.
502 Bad Gateway Backend connection Node.js process, bind address, port, and local firewall.
HTTP works but HTTPS fails TLS server block Certificate paths, port 443 listener, and certificate hostname.
API works but sockets fail WebSocket proxying HTTP/1.1 and Upgrade/Connection headers.

Following this order prevents a common mistake: changing application code when the request has not yet reached the VPS, or changing DNS when NGINX is serving the wrong virtual host. Resolve the outermost failing layer first, then retest before moving inward.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.