Recommended Free Tools
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.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
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.
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
- The browser requests
https://example.com/or an Angular client-side route such as/inventory/42. - NGINX checks the configured Angular root and returns an existing file. If no file exists,
try_filesinternally servesindex.html, allowing Angular’s router to handle the route. - The browser calls an API URL such as
/api/users. - NGINX matches the more specific
/api/location and proxies that request to the Node.js upstream. - 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.
#1 Best Overall
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.
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
- 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.
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
- Save the server block in the location used by your NGINX installation for site configurations, then enable it according to that installation’s layout.
- Confirm that the
rootdirectory exists and containsindex.html. - Test syntax before applying the change:
nginx -t
- Reload NGINX after a successful test. Use your operating system’s service manager and reload operation so existing connections can finish gracefully.
- Test the Node listener locally before diagnosing proxy behavior. For example, request the health endpoint on
127.0.0.1:3000from the server itself, using the path your application actually exposes. - 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.
Rank #3
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.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minutelocation /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
- 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.
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:
Best Value
- 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.
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.
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
rootpoints to the directory containingindex.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
browserdirectory 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 mappedConnectionheader. - Increase
proxy_read_timeoutwhen 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.htmland 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 -tpasses 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.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Quick Recap
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.




