One hostname can serve a website at /, an API at /api/ and a WebSocket endpoint at /socket/. The subtle part is agreeing whether the upstream receives those prefixes. A single trailing slash in proxy_pass can change that contract.
This tutorial targets Nginx on Ubuntu with three loopback services. It assumes HTTPS is already configured using the deployment walkthrough. The routing snippets belong inside your existing server block; do not replace its certificate configuration.
Write the URL contract first
Public URL Upstream receives
/ website:3000 /
/api/products?limit=5 API:3001 /products?limit=5
/socket/ realtime:3002 /socket/The API here is deliberately built without an /api prefix. The realtime service expects its prefix. These are choices, not universal conventions. Test redirects, cookies and generated asset URLs against the public path as well as the upstream path.
Strip only the prefix you intend to strip
location = /api {
return 308 /api/;
}
location ^~ /api/ {
proxy_pass http://127.0.0.1:3001/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}The slash after 3001 is a URI component. For this prefix location, Nginx replaces the matched /api/ part with /. Without that slash, the original /api/products path is preserved. The proxy_pass reference documents this distinction.
The exact /api location makes the missing-slash behaviour deliberate. A 308 preserves the method, but API clients should call the canonical path directly; some clients do not follow redirects as expected. ^~ prevents a later regular-expression location from unexpectedly taking over this prefix.
Forward WebSocket upgrades explicitly
Place the map in the http context, such as an Ubuntu conf.d file included from that context:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}Then add the location inside the same server block:
location ^~ /socket/ {
proxy_pass http://127.0.0.1:3002;
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-Forwarded-Proto $scheme;
proxy_read_timeout 75s;
}Upgrade headers are hop-by-hop and need explicit forwarding, as the Nginx WebSocket guide explains. Coordinate idle timeouts with application ping/pong behaviour. Increasing a timeout does not keep a dead connection alive.
Validate the WebSocket Origin and authorization in the application. A same-host URL is convenient, but it does not make every connection trustworthy.
Review the shared-origin consequences
All these paths share an origin. A script vulnerability in one application can affect other same-origin functionality. Cookie Path controls where a cookie is sent; it is not a complete isolation boundary between untrusted applications.
Use separate hostnames when applications require independent security boundaries or cannot reliably generate prefixed URLs. Next.js applications mounted below a path need their supported base-path configuration at build time; a proxy rewrite alone does not fix every asset and navigation URL.
Configure request-body limits, buffering and timeouts per route based on the actual workload. Do not give an entire domain an enormous upload limit because one authenticated endpoint needs it.
Verify the mapping
Run sudo nginx -t before reloading. In a test environment, make each upstream return its received pathname and confirm the three contract rows, query strings, trailing slashes and a nonexistent route. Remove diagnostic echo endpoints before production.
Test a real WebSocket handshake with the application's client, then leave it idle beyond the heartbeat interval. Check that API errors remain API errors rather than the website's fallback HTML.
The snippets were documentation-reviewed but not loaded into a running Nginx instance for this article. Next.js application development can include these routing and authentication boundaries.
