DEV Community

Cover image for Nginx proxy_pass: The trailing slash that changes everything
Schiff Heimlich
Schiff Heimlich

Posted on

Nginx proxy_pass: The trailing slash that changes everything

You know how sometimes you copy an nginx config snippet and it just... doesn't work? You stare at it, reload, check the logs, and nothing seems wrong. Then you spot it: a missing or extra slash at the end of the proxy_pass line. It’s one of those tiny details that silently breaks your setup.

Let’s look at two examples.

First, with a trailing slash:

location /app/ {
    proxy_pass http://backend/;
}
Enter fullscreen mode Exit fullscreen mode

Here, nginx replaces the matched part (/app/) with the URI in proxy_pass (http://backend/). So a request to /app/index.html goes to http://backend/index.html.

Now, without the trailing slash:

location /app/ {
    proxy_pass http://backend;
}
Enter fullscreen mode Exit fullscreen mode

In this case, nginx passes the entire request URI unchanged. So /app/index.html becomes http://backend/app/index.html.

The difference is subtle but important. If your backend expects requests at the root (like /index.html), the first form is what you want. If it expects the path to be preserved (like /app/index.html), the second form works.

I’ve seen this trip up people when they move from a location block that matches a prefix to one that’s more specific. The muscle memory of copying the proxy_pass line doesn’t account for the slash.

It’s not a bug. It’s documented behavior. But it’s easy to overlook when you’re in a hurry. Next time your proxied requests are going to the wrong place, take ten seconds to check that slash.

Top comments (1)

Collapse
 
crdtcto profile image
Kane Lim

This is a great example of how a one-character difference in infrastructure configuration can completely change application behavior.

The important detail is that proxy_pass with a URI component triggers nginx’s URI replacement behavior. With:

location /app/ { proxy_pass backend/; }

nginx effectively replaces the matched /app/ prefix, so /app/index.html becomes /index.html upstream.

Whereas:

location /app/ { proxy_pass backend; }

preserves the original request URI, sending /app/index.html upstream.

One additional detail worth remembering: the behavior becomes even more interesting when location uses regex or rewrite directives. In those cases, nginx may not have enough information to determine the replaced URI automatically, and explicit handling is often safer.

For production systems, I usually recommend validating this with an upstream access log that records $request_uri (client-side URI) alongside the upstream URI/status. It makes these routing issues immediately visible instead of debugging them from the application layer.

Small nginx details like this are exactly why reproducible configs, integration tests, and documented routing contracts matter. A 30-second config review can save hours of debugging.

Your explanation is concise and technically useful. 👍