The 2 AM Deployment NightmareYou just pushed a new microservice to production. Inside the container, everything looks perfect; a curl localhost:8080/api/health returns a 200 OK. But the moment you try to access it through your Nginx gateway, you hit a 404 Not Found. Even weirder, your backend logs show requests hitting mangled paths like //user/profile or /api/api/user/profile. Nginx is alive, the upstream is up, but the routing is broken.
The Error MessageLogs or automated monitoring tools usually report:
404 Not Found hoặc request bị chuyển hướng sai path khi dùng proxy_pass tới backend
The Logic: Why One Slash Changes EverythingNginx handles the proxy_pass directive differently based on a tiny detail: whether the URL contains a URI (a path or even just a trailing slash). This behavior is responsible for a massive percentage of reverse proxy configuration errors.
Scenario 1: No URI in proxy_pass (The "Pass-Through")When you omit the trailing slash after the hostname or port, Nginx passes the original request URI exactly as it was received. It doesn't modify the string.
location /api/ {
proxy_pass http://backend_server;
}
```- **Incoming Request:** `/api/users`- **Path Sent to Backend:** `/api/users`- **Best for:** Monoliths or backends that already expect the full prefix.### Scenario 2: URI Present in proxy_pass (The "Replacement")If you add a trailing slash (`/`) to the `proxy_pass`, Nginx treats it as a replacement. It calculates the part of the URI that **didn't** match the location block and appends it to the path defined in your `proxy_pass`.
location /api/ {
proxy_pass http://backend_server/;
}
```- Incoming Request: /api/users- Path Sent to Backend: /users- Note: The /api/ portion was stripped away and replaced by the / defined in the proxy_pass.## How to Fix Your Configuration### 1. Keep the Full PathIf your backend service (like a Spring Boot or Express app) is configured to listen on /api/..., do not use a trailing slash. This is the safest default for most microservices.
location /app/ {
proxy_pass http://127.0.0.1:8080;
}
A request to /app/login will hit your backend as /app/login.
2. Strip the Prefix CorrectlyMany standalone services, like a Prometheus dashboard or a specialized API, expect to live at the root (/). If you are mapping /service-a/ to a backend that doesn't know it's behind a proxy, you must use a trailing slash on both the location and the proxy_pass.
location /service-a/ {
proxy_pass http://service_a_upstream/;
}
Beware the Double Slash: If you use location /service-a (no slash) with proxy_pass http://host/ (with slash), a request to /service-a/login becomes //login. Many strict frameworks will reject this with a 404 or a 400 Bad Request.
3. The Regex AlternativeWhen routing logic gets messy, use a rewrite. This is often more readable than memorizing slash rules and prevents "off-by-one" errors in your paths.
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://backend_server;
}
Verification StepsStop guessing and start measuring. Use these tools to see exactly what Nginx is doing.
Check Upstream LogsDefine a custom log format in your nginx.conf. This reveals the exact path Nginx is sending to your backend servers.
log_format debug_proxy '$remote_addr - $request - to: $upstream_addr '
'uri: $uri path_sent: $request_uri';
access_log /var/log/nginx/access.log debug_proxy;
Inspect with CurlUse curl -Iv to check for 301 redirects. Often, Nginx will redirect /api to /api/ automatically, which can confuse frontend clients or API consumers.
curl -Iv http://your-domain.com/api/user

