Proxying Spring Boot and Node.js
Introduction
Most Hello Code backends are Spring Boot JARs on port 8080 or Node apps on port 3000. This chapter gives copy-ready Nginx configs for each stack, notes health checks, and links to deployment chapters so you can go from java -jar or node dist/index.js to a public URL behind Nginx.
Prerequisites
- Proxy Headers and Path Rules
- Reverse Proxy Basics
- App running locally for smoke tests
Spring Boot on 8080
Run the JAR bound to localhost
java -jar app.jar --server.address=127.0.0.1 --server.port=8080Or in application.properties:
server.address=127.0.0.1
server.port=8080
server.forward-headers-strategy=frameworkCode explanation:
server.address=127.0.0.1— not reachable from the internet except via Nginxforward-headers-strategy— honorX-Forwarded-*for redirects and links
Nginx server block
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
include /etc/nginx/proxy_params;
}
}Test:
curl http://api.example.com/actuator/health
curl http://api.example.com/api/your-endpointAdjust paths to your controllers. See the Spring Boot track for application setup.
If your team deploys a Spring Boot WAR to shared Tomcat instead of java -jar, see the Tomcat track and Nginx + Tomcat project—same proxy_pass idea, but mind WAR context paths (e.g. /api/hello not /hello).
Actuator health (optional)
If Spring Boot Actuator is enabled:
location /actuator/health {
proxy_pass http://127.0.0.1:8080/actuator/health;
include /etc/nginx/proxy_params;
access_log off;
}Use for load balancer or monitoring probes—restrict public access in production (management.endpoints.web.exposure).
Context path /api
When server.servlet.context-path=/api:
location / {
proxy_pass http://127.0.0.1:8080;
include /etc/nginx/proxy_params;
}Public URL: https://api.example.com/api/... matches Spring’s context path without Nginx stripping.
Node.js on 3000
Express example
const express = require("express");
const app = express();
app.get("/health", (_req, res) => {
res.json({ ok: true });
});
app.listen(3000, "127.0.0.1", () => {
console.log("Listening on 127.0.0.1:3000");
});Code explanation:
- Listen on
127.0.0.1only—same security pattern as Java
Nginx config
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
include /etc/nginx/proxy_params;
}
}See Deploying Node applications for PM2, env vars, and production process management.
Trust proxy in Express
app.set("trust proxy", 1);Code explanation:
- Enables correct
req.ipand secure cookies behind Nginx - Value
1means one proxy hop—adjust for your topology
Combined: SPA + Spring Boot API
Code explanation:
- Static Vite/React build at
/ - API under
/api/forwarded to Spring on 8080 with prefix strip
Verify:
curl -I http://shop.example.com/
curl http://shop.example.com/api/productssystemd + Nginx Workflow
Typical VPS flow:
- systemd unit starts JAR or Node on boot (
127.0.0.1:8080) - Nginx starts on 80/443
- Deploy: build app → restart app unit → optional static
rsync→nginx -t && reload
Quick check both layers:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/actuator/health
curl -s -o /dev/null -w '%{http_code}\n' -H "Host: api.example.com" http://127.0.0.1/Both should return 200 (or your app’s expected code).
WebSocket Preview
Node Socket.IO or Spring STOMP may need:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";Full WebSocket chapter covers this in depth.
FAQ
Spring Boot on 8080 but Nginx 502?
JAR not running, wrong port, or bound only to wrong interface—ss -tlnp | grep 8080.
Node with PM2 on 3000?
Same Nginx config—PM2 keeps Node alive; Nginx still proxies to 127.0.0.1:3000.
Separate subdomains vs /api path?
api.example.com → 8080 and www.example.com → static is cleanest CORS-wise. Single domain /api/ is simpler DNS.
gzip on JSON API?
Often enabled in http block—see gzip chapter; small JSON benefit is modest.
HTTPS before or after this chapter?
Get HTTP proxy working first; next HTTPS chapter adds listen 443 ssl.
Link to Linux quick deploy?
Linux chapter 15 is a shorter path; this track adds headers, paths, and split SPA/API patterns.