nginx
proxy_pass
404 error
web server
troubleshooting

nginx proxy_pass leads to 404 Not Found page

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When proxy_pass leads to a 404 Not Found, the problem is often not Nginx itself. The request is usually reaching the upstream application with a path the application does not recognize. That mismatch commonly comes from the interaction between location matching and the exact form of the proxy_pass URL.

The most important detail is the trailing slash. In Nginx, a trailing slash on proxy_pass changes how the request URI is rewritten before it is sent upstream.

The Trailing Slash Rule

Consider this configuration:

nginx
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
}

With the trailing slash on proxy_pass, Nginx replaces the matched /api/ prefix with / before forwarding the request. So a request for /api/users becomes /users on the upstream server.

Now compare it with this:

nginx
location /api/ {
    proxy_pass http://127.0.0.1:3000;
}

Without the trailing slash, the original URI is forwarded as-is. A request for /api/users stays /api/users on the upstream.

If your application expects /users but receives /api/users, it returns 404. If it expects /api/users but receives /users, it also returns 404.

Diagnose Where the 404 Comes From

Before editing configuration blindly, confirm whether the 404 is generated by Nginx or by the upstream service.

Useful checks include:

bash
curl -i http://localhost/api/users
curl -i http://127.0.0.1:3000/users
curl -i http://127.0.0.1:3000/api/users

If one upstream path works and the other fails, you have identified the rewrite mismatch immediately.

You should also inspect logs:

bash
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

An upstream framework log is often even more useful because it shows the exact path the application received.

A Common Correct Pattern

If your public route is /api/... but the upstream app serves routes without that prefix, use:

nginx
1location /api/ {
2    proxy_pass http://127.0.0.1:3000/;
3    proxy_set_header Host $host;
4    proxy_set_header X-Real-IP $remote_addr;
5    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6    proxy_set_header X-Forwarded-Proto $scheme;
7}

If the upstream app already includes /api/... in its route definitions, remove the trailing slash from proxy_pass so the prefix is preserved.

Avoid Manual Rewrite Confusion

It is possible to combine rewrite directives with proxy_pass, but that often makes troubleshooting harder than necessary. In many cases, choosing the correct trailing-slash form is enough.

If you do need explicit rewriting, keep it obvious:

nginx
1location /api/ {
2    rewrite ^/api/(.*)$ /$1 break;
3    proxy_pass http://127.0.0.1:3000;
4}

That can work, but it adds one more moving part. Simpler configurations are easier to reason about and maintain.

Other Reasons for Upstream 404

Path rewriting is the most common cause, but not the only one. A proxied request can still return 404 if:

  • the upstream app is listening on a different port
  • the upstream virtual host depends on the Host header
  • the location block does not match the request you think it matches
  • a framework router only accepts certain methods

That is why direct curl tests against the upstream port are so useful. They separate routing problems from proxy configuration problems.

Common Pitfalls

The biggest pitfall is not realizing that proxy_pass http://backend/; and proxy_pass http://backend; do different things. That one character changes the forwarded URI.

Another issue is testing only through Nginx and never calling the upstream directly. Without direct upstream tests, it is hard to tell whether the route exists at all.

People also forget that a 404 can be application-generated. If Nginx is configured correctly but the backend router does not define the forwarded path, the result is still 404.

Finally, after editing Nginx, always validate and reload the config:

bash
nginx -t
sudo systemctl reload nginx

Summary

  • A 404 after proxy_pass is often caused by URI rewriting rather than by Nginx failing to proxy.
  • The trailing slash on proxy_pass controls whether the matched location prefix is replaced.
  • Test the upstream path directly to confirm what route the backend actually serves.
  • Add standard proxy headers so the upstream receives the expected request context.
  • Prefer simple location and proxy_pass combinations before adding separate rewrite rules.

Course illustration
Course illustration

All Rights Reserved.