Nginx Reverse Proxy: Removing URL Prefix (/foo) Without 302 Redirect


4 views

When implementing reverse proxies in Nginx, a common requirement is to strip away a URL prefix before forwarding requests to backend services. Here's the standard scenario:

location /foo {
    proxy_pass http://localhost:3200;
    proxy_redirect off;
    proxy_set_header Host $host;
}

This configuration forwards /foo/bar to the backend as /foo/bar, but many applications expect the path without the prefix.

Many developers first try using rewrite with permanent flags:

rewrite ^(.*)foo(.*)$ http://localhost:3200/$2 permanent;

This creates unwanted 302/301 redirects instead of internal path processing. The client sees the URL change in their browser - not what we want for API endpoints or web apps.

For seamless prefix removal without redirects, use this pattern:

location /foo {
    proxy_pass http://localhost:3200/; # Note trailing slash
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

The magic happens with:

  • proxy_pass with trailing slash: strips the matched prefix
  • No rewrite rules needed
  • Preserves original request headers

For more complex patterns, use regex locations:

location ~ ^/foo(/.*)?$ {
    proxy_pass http://backend:3200$1;
    # Additional proxy settings...
}

Verify with curl:

curl -v http://localhost/foo/api/test

Check that:

  1. The response comes directly (no 301/302)
  2. Your backend receives /api/test
  3. Original headers are preserved

When setting up Nginx as a reverse proxy, you might encounter situations where you need to remove a path prefix before forwarding requests to your backend application. The common approach using rewrite with permanent flag results in a 302 redirect, which isn't always desirable.

The rewrite directive with the permanent flag (or its numeric equivalent 301) is designed to tell clients to make a new request to the rewritten URL. This is fundamentally how HTTP redirects work.

Instead of using rewrite, we can leverage Nginx's proxy_pass with URI manipulation:

location /foo {
    proxy_pass http://localhost:3200/;
    proxy_redirect off;
    proxy_set_header Host $host;
}

The key difference is the trailing slash after the proxy_pass URL. This tells Nginx to replace the matched prefix (/foo) with the specified URI (/).

Here's a more complete example with additional common proxy settings:

server {
    listen 80;
    server_name example.com;

    location /foo/ {
        proxy_pass http://localhost:3200/;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Optional: Timeout settings
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

After making changes, always test your configuration:

sudo nginx -t
sudo systemctl reload nginx

Then verify with curl:

curl -v http://localhost/foo/bar

You should see the request being proxied to your backend at /bar without any redirects.

For more complex scenarios where you need regex pattern matching, you can use:

location ~ ^/foo(/.*)$ {
    proxy_pass http://localhost:3200$1;
    # ... other proxy settings
}

This captures everything after /foo in $1 and passes it to the backend.

The solution using simple prefix matching (location /foo/) is more efficient than regex-based approaches. Nginx processes prefix locations faster, especially under heavy load.