Recommended Free Tools
To deploy a job application web app with Nginx, put Nginx on the public interface, let it serve immutable files such as CSS and JavaScript, and proxy API and page requests to a separate application process. This guide uses an example stack of Ubuntu, Nginx, and a Node.js/Express app listening on 127.0.0.1:3000. The same boundary works with another runtime after you change the service command and upstream address.
The request path is: browser → Nginx on ports 80/443 → static files or the application server. Nginx is the web server and reverse proxy, not the JavaScript runtime or job-application business logic. Its role and proxy model are documented in the NGINX web-server guide and the NGINX Node.js deployment guide.
As an Amazon Associate I earn from qualifying purchases.
Choose the deployment boundary before installing anything
Assume a single Linux server with these components:
Free tools Windows power users keep installed
One-click scans. No signup required.
- Nginx: installed on the host and exposed to the Internet on TCP 80 and 443.
- Application: a Node.js/Express process bound only to
127.0.0.1:3000, managed by systemd. - Static release directory:
/var/www/job-app/current/public, containing the built browser assets. - Application release directory:
/var/www/job-app/current, containing the server code and its production dependencies.
Only Nginx needs a public listener. A browser request for /assets/app.css can be answered from disk. A request for /, /jobs, /api/applications, or another dynamic route is sent to Express. Keep the application listener private; this makes Nginx the single place for TLS termination, request limits, access logging, and routing.
#1 Best Overall
This example does not decide how candidate records, uploaded résumés, email, databases, backups, or privacy obligations are implemented. Those are application and jurisdiction decisions, not consequences of choosing Nginx.
Install Nginx and prepare the application
Install the host packages
On an Ubuntu server, install Nginx and the Node.js runtime using your organisation’s approved package source. The exact Node.js major version is a release decision for your application; use the version declared by its package.json and lockfile.
sudo apt update
sudo apt install nginx
sudo systemctl enable --now nginx
Copy the application into a versioned release directory, install production dependencies, and build the front end if the project has a build step. For example:
sudo mkdir -p /var/www/job-app/releases/2026-10-18
sudo chown -R $USER:$USER /var/www/job-app
cd /var/www/job-app/releases/2026-10-18
# copy your source and lockfile here
npm ci --omit=dev
npm run build
ln -sfn /var/www/job-app/releases/2026-10-18 /var/www/job-app/current
Do not run the server as root. Create a dedicated account and make that account the owner of the application files. If your build emits static files into a different directory, use that directory in the Nginx configuration instead of guessing.
Make the app listen on a private address
Configure Express to read HOST and PORT from the environment, with safe defaults:
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
const host = process.env.HOST || '127.0.0.1';
const port = Number(process.env.PORT || 3000);
app.listen(port, host, () => {
console.log(`listening on ${host}:${port}`);
});
Binding to loopback prevents a remote client from bypassing Nginx on this host. If the app and Nginx are separate machines, bind to a private interface and restrict that network path with a firewall instead.
Run the app as a managed service
A terminal session is not a deployment strategy: it ends when you disconnect and does not reliably restart after a crash or reboot. A systemd unit gives the process a defined working directory, environment, user, and restart policy.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteCreate an environment file
sudo install -d -m 750 /etc/job-app
sudoedit /etc/job-app/job-app.env
Put non-public runtime settings in that file, for example:
NODE_ENV=production
HOST=127.0.0.1
PORT=3000
Do not place database passwords or signing keys in a repository. Set ownership and mode so only root and the service account can read the file.
Create the systemd unit
sudoedit /etc/systemd/system/job-app.service
[Unit]
Description=Job application web service
After=network.target
[Service]
Type=simple
User=jobapp
Group=jobapp
WorkingDirectory=/var/www/job-app/current
EnvironmentFile=/etc/job-app/job-app.env
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Adjust ExecStart to the actual entry point produced by your project. Then enable and inspect it:
Rank #3
sudo systemctl daemon-reload
sudo systemctl enable --now job-app
sudo systemctl status job-app
sudo journalctl -u job-app -n 100 --no-pager
Before adding Nginx, verify locally on the server with curl http://127.0.0.1:3000/health if your app provides a health endpoint. A connection refusal means the application process, command, port, or bind address needs fixing; it is not an Nginx problem yet.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Configure Nginx to split static and dynamic traffic
Nginx configuration normally lives in /etc/nginx/nginx.conf with site files included from a subordinate directory. Create a site definition and use one explicit upstream for the single app instance.
sudoedit /etc/nginx/sites-available/job-app
upstream job_app_backend {
server 127.0.0.1:3000;
}
server {
listen 80;
listen [::]:80;
server_name jobs.example.com;
root /var/www/job-app/current/public;
location /assets/ {
try_files $uri =404;
access_log off;
add_header Cache-Control "public, max-age=31536000, immutable";
}
location / {
proxy_pass http://job_app_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;
}
}
Enable the site and remove the distribution’s default site if it would answer the same hostname:
sudo ln -s /etc/nginx/sites-available/job-app /etc/nginx/sites-enabled/job-app
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
The try_files rule returns a 404 when an asset is absent, while the catch-all location forwards dynamic requests. If your front end uses client-side routing, let the application return its index document or deliberately add a fallback such as try_files $uri $uri/ /index.html; do not add that fallback to an API location, where it can hide real 404 responses.
Why the forwarded headers matter
The app receives the original host, client address chain, and HTTP/HTTPS scheme through headers. Configure Express’s proxy trust setting only for the proxies you actually operate; trusting arbitrary client-supplied forwarding headers can make IP-based controls and secure-cookie decisions unreliable. Use the scheme header when constructing callback URLs, but validate allowed hostnames in the application.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →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
Use reloads as a guarded deployment operation
Nginx does not replace a working configuration blindly. Its master process checks the new configuration and attempts to apply it; if the new configuration cannot be applied, the old configuration continues serving traffic. The NGINX Beginner’s Guide documents this reload behavior.
Use this release sequence:
- Upload the new application to a new, versioned release directory.
- Run the dependency install and build there, without changing the current symlink.
- Run the application’s checks and a local request against the release.
- Switch
/var/www/job-app/currentto the new release. - If Nginx paths or headers changed, run
sudo nginx -t. - Reload with
sudo systemctl reload nginx; do not use a restart when a reload is sufficient. - Restart the app service when its code or environment changed:
sudo systemctl restart job-app. - Check the systemd journal, Nginx error log, and an external HTTPS request.
Keep the previous release until the new one is proven. A rollback is then a symlink switch followed by an app restart, rather than a hurried rebuild on the live directory.
Read the right log for the right symptom
- 502 Bad Gateway: Nginx cannot connect to the upstream. Check that systemd reports the app as running and that it is listening on the configured address and port.
- 404 for CSS or JavaScript: verify the release’s
publicpath, file permissions, and the URL generated by the front end. - Redirect loop or wrong callback URL: inspect
X-Forwarded-Proto, Express proxy trust, and any application HTTPS redirect. - Nginx fails to reload: run
sudo nginx -tand read the reported file and line. Because reload is guarded, the previous valid configuration remains active while you correct the error.
Add HTTPS without exposing the private key
For production traffic, obtain a certificate for the hostname from a certificate authority and place the certificate chain and private key on the server. Nginx’s HTTPS configuration guide shows a server listening on 443 with ssl_certificate and ssl_certificate_key.
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name jobs.example.com;
ssl_certificate /etc/ssl/job-app/fullchain.pem;
ssl_certificate_key /etc/ssl/job-app/private.key;
root /var/www/job-app/current/public;
location /assets/ {
try_files $uri =404;
}
location / {
proxy_pass http://job_app_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;
}
}
server {
listen 80;
listen [::]:80;
server_name jobs.example.com;
return 301 https://$host$request_uri;
}
The certificate is public; the private key is not. Restrict the key file so unrelated users cannot read it, while ensuring the Nginx master process can read it during startup and reload. Test permissions and the complete chain before switching the site to 443.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
The Nginx guide describes TLS 1.2 and TLS 1.3 and cipher defaults such as HIGH:!aNULL:!MD5, but also notes that defaults have changed over time. Treat those values as version-dependent: inspect the documentation for the Nginx and TLS libraries installed on your server rather than assuming an eternal default. After editing TLS settings, run sudo nginx -t and test both the hostname and certificate renewal process.
Best Value
Optional route: run the application with Docker Compose
Compose is useful when the application, worker, and supporting services are released as containers. Docker describes a single-server Compose deployment as the easiest path, with clustering as a separate option. You can keep Nginx on the host and publish only the app’s loopback port:
services:
web:
build: .
environment:
NODE_ENV: production
HOST: 0.0.0.0
PORT: 3000
ports:
- "127.0.0.1:3000:3000"
restart: unless-stopped
Inside the container, 0.0.0.0 is required so the container interface can receive traffic; the host-side binding to 127.0.0.1 still prevents remote clients from reaching it directly. If Nginx is also containerized, put both services on a private Compose network and proxy to the service name instead of publishing the app port.
Docker states, “Publishing container ports is insecure by default.” Its port publishing documentation explains that a published port is normally reachable beyond the host; binding to 127.0.0.1 or ::1 limits access to the Docker host. The documentation also notes a localhost-publishing caveat for Docker releases older than 28.0.0 when another device shares the same layer-2 segment, so keep Docker and host firewall rules current.
Separate development and production settings
Use a production override or a separate production Compose file to remove source-code bind mounts, set production environment values, add a restart policy, and attach log handling appropriate to your service. Do not copy development database credentials into production. Persist data deliberately and define backups, migrations, secrets, and health checks for the actual application; Compose cannot infer those policies.
When releasing changed code, rebuild and recreate the changed service rather than expecting a running container to update:
docker compose -f compose.yml -f compose.production.yml build web
docker compose -f compose.yml -f compose.production.yml up -d --no-deps web
After recreation, verify the container logs, the host-side Nginx upstream, and an external request. If Nginx and Compose are on different hosts, replace the loopback binding with a private address and enforce network access with firewall rules.
One instance or several?
A single upstream is the simplest release and troubleshooting model. If the app becomes CPU-bound or requires redundancy, Nginx can distribute requests across multiple application instances, as illustrated by the upstream pattern in the NGINX Node.js deployment guide:
upstream job_app_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
Multiple instances add operational work: each process needs a distinct port or container, compatible environment and code, coordinated migrations, and a strategy for sessions and uploaded files. Do not assume that adding servers fixes a database bottleneck or makes local files shared.
Quick Recap
| Choice | Operational shape | Network exposure | Release action |
|---|---|---|---|
| Host Nginx + systemd app | Few moving parts; familiar Linux logs and service controls | Only Nginx is public; app binds to loopback | Switch release, restart app, reload Nginx when needed |
| Host Nginx + Compose app | Reproducible image and dependencies; container lifecycle to manage | Bind published app port to loopback or a private interface | Build and recreate the changed service |
| Several app instances | Capacity or redundancy with added session and data concerns | Keep every upstream private | Roll instances carefully and verify compatibility |
Production checklist for this architecture
- DNS points the application hostname to the server that runs Nginx.
- Nginx is the only service intentionally reachable on public web ports.
- The app listens on loopback or a private network, not an unrestricted public interface.
nginx -tsucceeds before every reload.- The certificate chain is valid, the private key is restricted, and Nginx’s master can read it.
- Forwarded headers and Express proxy trust are configured consistently.
- Application, Nginx access, and Nginx error logs are retained and monitored.
- Releases are versioned so the previous code can be restored quickly.
- Container ports are audited; no development port is published accidentally.
- Database persistence, candidate-file storage, backups, retention, access controls, and privacy compliance are documented separately for the job-application domain.
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.

