Tools

nginx proxy_pass URI simulator

Enter a location, a proxy_pass value and a request URI, and see the exact upstream request nginx will make, plus the trailing slash trap that changed it.

Runs entirely in your browser · nothing is uploaded · updated 8 September 2026

Work out the upstream URI

The rule, in one sentence#

If proxy_pass contains a URI part, meaning anything after the host and optional port including a bare /, nginx replaces the portion of the request URI that matched the location with that URI. If proxy_pass has no URI part, the request URI is forwarded unchanged and nothing is stripped.

Everything people call "the trailing slash thing" follows from that one sentence. The simulator above applies it to your exact values and shows the resulting upstream request line.

The four cases people hit#

locationproxy_passRequestUpstreamComment
/api/http://backend//api/v1/users/v1/usersURI part / replaces /api/, the prefix is stripped
/api/http://backend/api/v1/users/api/v1/usersNo URI part, nothing is stripped
/apihttp://backend//api/v1/users//v1/usersMatched prefix /api becomes /, leaving a double slash
/api/http://backend/v2//api/v1/users/v2/v1/usersReplacement is literal, not a merge

The third row is the one that produces a 404 nobody can explain. Many backends treat //v1/users as a different path from /v1/users, and some frameworks 301 it, which then breaks POST bodies.

What the simulator will not tell you#

When proxy_pass contains a variable#

Using a variable, for example proxy_pass http://$upstream;, changes three things at once, and the simulator flags all three:

  1. No prefix replacement happens. The value is used exactly as written after substitution.
  2. nginx resolves the host at request time, so a resolver directive must be in scope or the request fails with "no resolver defined to resolve ...".
  3. A name that matches an upstream {} block is no longer looked up as an upstream group in the usual way, so load balancing and keepalive settings you configured there stop applying.

That combination is also the classic accidental open proxy, covered in open proxies and misconfigured relays.

Frequently asked questions#

Why does adding a trailing slash to proxy_pass change the URL?#

Because a trailing slash is a URI part. With proxy_pass http://backend/ nginx replaces the matched location prefix with /, stripping it. With proxy_pass http://backend there is no URI part, so the full request URI is passed through.

Why am I getting a double slash at the upstream?#

Your location does not end in a slash but your proxy_pass URI does, so the matched prefix /api is replaced by / and the remaining /v1/users is appended to it. Make the trailing slashes match on both sides.

Can I use proxy_pass with a URI inside a regex location?#

No. nginx refuses to start, because there is no literal prefix to replace. Use rewrite ^/api/(.*)$ /$1 break; before a proxy_pass with no URI part, or build the target URI explicitly.

Does the query string get rewritten too?#

No. $args is passed through untouched. Only the path portion participates in the prefix replacement.

Which Host header does the upstream see?#

By default nginx sends Host: $proxy_host, which is the host from proxy_pass, not the client's Host. If your backend does name based virtual hosting or builds absolute URLs, set proxy_set_header Host $host; explicitly. The consequences are covered in X-Real-IP, X-Forwarded-Proto, Host and Port.

Primary sources#

Every normative claim on this page is checked against the specification or the vendor documentation listed here. Where behaviour is version dependent, the version is named in the text.

  1. nginx ngx_http_proxy_module, proxy_pass
  2. nginx ngx_http_core_module, location
  3. nginx request processing, server and location selection

More in proxy tools#