Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Set Up an Nginx Reverse Proxy for Node.js and Angular Apps

A practical NGINX configuration for serving an Angular production build, forwarding API requests to Node.js, handling HTTPS and WebSockets, and troubleshooting route and proxy errors.
Length10 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use NGINX as the public entry point: serve Angular’s production files from disk, and forward only an API path such as /api/ to the Node.js process listening on a private port. Add an SPA fallback for Angular routes, terminate HTTPS at NGINX, and explicitly pass WebSocket upgrade headers when the application needs them.

The configuration below is a production-oriented baseline. Replace the hostname, Angular build directory, certificate paths, and Node.js port with the values used by your server.

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.

How the reverse-proxy layout works

A browser connects to NGINX on the public hostname. NGINX returns Angular’s compiled JavaScript, CSS, images, and index.html directly from the filesystem. Requests beginning with /api/ are sent to Node.js instead of being treated as static files. The Node process can therefore listen on 127.0.0.1:3000 without exposing that port to the internet.

Request flow

  1. The browser requests https://example.com/ or an Angular client-side route such as /inventory/42.
  2. NGINX checks the configured Angular root and returns an existing file. If no file exists, try_files internally serves index.html, allowing Angular’s router to handle the route.
  3. The browser calls an API URL such as /api/users.
  4. NGINX matches the more specific /api/ location and proxies that request to the Node.js upstream.
  5. For a WebSocket endpoint, NGINX also forwards the HTTP upgrade negotiation and keeps the connection open for the configured timeout.

Deployment variants

Decision Baseline When the other option is appropriate
Angular delivery Static files built by Angular CLI and served by NGINX Server-side rendering requires a Node-rendering route and a different NGINX split; the static configuration here does not render Angular on the server.
Node upstream One listener at 127.0.0.1:3000 Multiple processes or hosts can be listed in an NGINX upstream group for round-robin distribution.
Transport HTTPS terminated at NGINX, with port 80 redirected HTTP-only operation is suitable only where TLS is terminated elsewhere or the environment is intentionally non-production.
Real-time traffic Ordinary HTTP proxying WebSockets need HTTP/1.1 plus explicit Upgrade and Connection headers.
Development Angular CLI proxyConfig That proxy belongs to ng serve; it does not replace the production NGINX server block.

Prerequisites and values to collect

  • NGINX installed on the server and permission to edit its site configuration.
  • A production Angular build and the exact directory containing its generated index.html.
  • A running Node.js application and the local address and port on which it listens.
  • A DNS name that resolves to the NGINX host if the service will be public.
  • A TLS certificate and private key for production HTTPS. Their issuance, renewal process, and filesystem locations depend on your operating system and certificate authority.
  • The API prefix and any WebSocket prefix used by the application. Keep these prefixes distinct from Angular’s static routes.

In the examples, example.com, /var/www/angular/dist/my-app/browser, and port 3000 are placeholders. Verify each value rather than copying it unchanged.

Build Angular for production and place the files

1. Create the production output

Run the Angular CLI production build from the application directory:

ng build

Angular writes the distribution output under the project’s configured dist directory. Recent Angular projects commonly place browser files in a nested path such as dist/my-app/browser; older or customized projects may use a different layout. Find the directory that contains index.html and use that directory as NGINX’s root.

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

2. Copy or deploy the output

Copy the generated browser files to a stable release directory on the NGINX host. The web-server account must be able to read the files and traverse every parent directory. Do not point root at the Angular source tree or at a directory that lacks the generated index.html.

3. Check the base URL and API paths

The browser application must call the same public API prefix configured in NGINX, for example /api/. A relative URL keeps development and production origins aligned; an absolute URL hard-codes a host and can bypass the reverse proxy. If your Angular build uses a different base href or deploy URL, ensure it matches the public path at which NGINX serves the application.

Configure NGINX to serve Angular and proxy Node.js

Create a server block with an upstream group and two locations. The static location handles Angular files and client-side routes; the API location forwards requests to Node.js.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option
upstream node_backend {
    server 127.0.0.1:3000;
}

server {
    listen 80;
    server_name example.com;

    root /var/www/angular/dist/my-app/browser;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

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

Why the SPA fallback matters

Angular navigation is usually handled in the browser. A direct request for /inventory/42 does not correspond to a physical file, so a plain static server would return 404. try_files $uri $uri/ /index.html; first serves real assets and then falls back to the Angular shell. Without this fallback, links that work after navigating inside the app can fail when refreshed or opened directly.

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

How proxy_pass changes the URI

The location and the form of proxy_pass together determine the path that Node.js receives. With location /api/ and proxy_pass http://node_backend; (no URI after the upstream name), NGINX forwards the original normalized request path, so /api/users reaches Node.js as /api/users.

If you instead write proxy_pass http://node_backend/;, the URI / replaces the part matched by /api/; /api/users then reaches the upstream as /users. Choose the form that matches your Node.js routes. A mismatch commonly produces a 404 even though the process is healthy.

Forward the original request context

NGINX changes some request headers by default. The explicit headers in the example preserve the public host, client address, proxy chain, and original HTTP or HTTPS scheme. Node.js frameworks use these values for absolute URLs, secure-cookie decisions, logging, redirects, and rate-limit or audit information. Configure your framework to trust the proxy only according to its own security model; do not blindly trust client-supplied forwarding headers when traffic can reach the application without passing through NGINX.

Enable the configuration safely

  1. Save the server block in the location used by your NGINX installation for site configurations, then enable it according to that installation’s layout.
  2. Confirm that the root directory exists and contains index.html.
  3. Test syntax before applying the change:
nginx -t
  1. Reload NGINX after a successful test. Use your operating system’s service manager and reload operation so existing connections can finish gracefully.
  2. Test the Node listener locally before diagnosing proxy behavior. For example, request the health endpoint on 127.0.0.1:3000 from the server itself, using the path your application actually exposes.
  3. Request the public hostname and inspect both NGINX’s error log and the Node.js application log while testing.

nginx -t checks configuration validity; it does not prove that the Angular path is correct, that Node.js is running, or that an API route returns the expected status.

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.

Add HTTPS with TLS termination at NGINX

For production, redirect clear-text HTTP to HTTPS and place the certificate on the TLS server block. Certificate issuance and renewal are provider- and operating-system-specific, so use the paths and renewal procedure supplied for your certificate.

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

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    root /var/www/angular/dist/my-app/browser;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

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

Keep the Angular and API locations in the HTTPS block. The X-Forwarded-Proto value tells Node.js that the browser used HTTPS even though the NGINX-to-Node hop may be plain HTTP on the local machine. Test redirects, secure cookies, and any framework-generated absolute URL after enabling TLS.

Proxy WebSockets correctly

WebSockets begin as an HTTP request and then upgrade the connection. NGINX needs HTTP/1.1 and the upgrade headers; ordinary proxy settings alone are insufficient. Define this map in the HTTP context, alongside your server blocks:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Add a location for the WebSocket path inside the relevant server block. The path shown is an example for Socket.IO-style traffic; use the endpoint your application actually exposes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
location /socket.io/ {
    proxy_pass http://node_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $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;
    proxy_read_timeout 60m;
}

NGINX closes an otherwise idle proxied connection after 60 seconds by default when no data arrives from the upstream. Increase proxy_read_timeout for long idle periods, as in the example, or have the application send ping frames. A longer timeout consumes connection capacity, so set it to the longest idle interval your application genuinely needs.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

Development proxy versus production NGINX

During local development, Angular CLI can divert matching requests from the development server to Node.js with a proxyConfig file on the serve target. A minimal example is:

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false,
    "changeOrigin": true
  }
}

Start the development server with the project’s configured proxy file, for example:

ng serve --proxy-config src/proxy.conf.json

This avoids browser cross-origin requests while coding, but it exists only in the Angular development workflow. A production build served by NGINX does not read that file. Production routing still requires the NGINX location and proxy_pass rules described above.

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

Scale the Node.js upstream when one process is not enough

NGINX can distribute requests across several Node.js listeners by adding servers to the upstream group:

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

Round robin is NGINX’s default load-balancing method for an upstream group. Scaling safely requires application decisions that the proxy cannot make for you:

  • Provide a health-check and removal strategy for failed processes; a listed server is not automatically proof that the application is healthy.
  • Decide where sessions and other mutable state live. In-memory sessions can send a user to a different process on the next request unless you use shared storage or an intentional affinity strategy.
  • Ensure uploads, generated files, and caches are available consistently to every process that may receive a request.
  • Use graceful shutdown and deployment sequencing so existing connections, especially WebSockets, are not cut off unexpectedly.
  • Review timeout, retry, and failure behavior before adding more upstreams; more processes do not correct an incorrect route or a broken application.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Caching and header decisions

NGINX can cache eligible upstream responses and reduce repeated work, but API caching is a correctness decision, not a switch to enable indiscriminately. User-specific, authorization-protected, or rapidly changing responses generally require an explicit cache policy. Decide which methods, status codes, cookies, and authorization headers are safe before adding proxy-cache directives.

For static Angular assets, immutable, content-hashed filenames can usually receive longer browser cache lifetimes than index.html. Apply cache headers only after confirming how your build names files and how releases invalidate the application shell. Do not cache index.html so aggressively that users remain on an obsolete deployment.

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

Forward only the headers your Node.js framework needs, and define a deliberate policy for client-provided forwarding headers. Log the public host, request path, upstream status, and correlation identifier if your operational tooling supports them; those fields make proxy failures much easier to distinguish from application failures.

Troubleshoot failures in the order they occur

NGINX returns 404 for every Angular route except the home page

  • Verify the configured root points to the directory containing index.html, not merely to its parent.
  • Confirm the location contains try_files $uri $uri/ /index.html;.
  • Check that the requested hostname reaches the intended server block rather than a default block.

Static files return 404 or 403

  • Check the exact generated path and filename, including the nested browser directory used by some Angular builds.
  • Verify read and directory-traversal permissions for the NGINX worker account.
  • Inspect the NGINX error log for the filesystem path it attempted to open.

API requests never reach Node.js

  • Test the Node listener locally on its configured address and port.
  • Confirm the request matches the intended prefix, such as /api/, and is not being consumed by the generic / location.
  • Run nginx -t, reload after changes, and watch NGINX and Node.js logs together.

Node.js receives the wrong path

Compare the location and proxy_pass forms. A trailing URI changes replacement behavior: proxy_pass http://node_backend; preserves /api/users, while proxy_pass http://node_backend/; strips the matched /api/ prefix and sends /users.

Redirects, cookies, or generated URLs use HTTP

Confirm the HTTPS server block sets proxy_set_header X-Forwarded-Proto $scheme; and that the Node.js framework is configured to recognize the trusted proxy. Also verify that the browser is reaching the TLS server block after the port-80 redirect.

WebSocket connections close after about one minute

  • Confirm the request matches the WebSocket location.
  • Check proxy_http_version 1.1, Upgrade, and the mapped Connection header.
  • Increase proxy_read_timeout when long idle periods are expected, or send application-level ping frames.
  • Check both browser developer tools and Node.js logs for handshake status and disconnect reasons.

Pre-release verification checklist

  • Angular: the configured root contains the intended production index.html and assets.
  • Routing: a deep link and a browser refresh return the Angular shell instead of a 404.
  • API: a local Node.js health request succeeds, then the same operation succeeds through the public /api/ path.
  • URI: the Node.js route matches the chosen trailing-URI behavior of proxy_pass.
  • Headers: host, client address, forwarding chain, and scheme are available to the framework as intended.
  • TLS: port 80 redirects to HTTPS, the certificate paths are readable, and secure-cookie behavior is correct.
  • WebSockets: upgrade headers are present and idle connections remain open for the required interval.
  • Operations: nginx -t passes before reload, and NGINX plus Node.js logs are available during rollout.

What this setup gives you

With the static Angular root, SPA fallback, dedicated API location, explicit forwarding headers, and optional TLS and WebSocket blocks in place, NGINX cleanly separates browser assets from Node.js application traffic. You can keep Node.js private, change the Angular release directory independently, and expand the upstream group later without changing the public hostname. The details that must remain application-specific are the build path, API and WebSocket prefixes, URI rewrite choice, certificate lifecycle, session strategy, and timeout values.

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

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.

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 quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

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.