How to Properly Route HTTP Requests to Different Ports in Nginx with Path Rewriting


2 views

When working with Nginx as a reverse proxy, a common requirement is to forward requests from one path to a different backend service while modifying the request path. The key challenge emerges when you need to remove or rewrite parts of the original URI during the proxy pass.

Consider this common but problematic configuration:

location /route {
    proxy_pass http://127.0.0.1:9000;
}

This forwards http://localhost/route/abc to http://localhost:9000/route/abc when what we actually want is http://localhost:9000/abc.

Here are three effective approaches to solve this routing issue:

1. Using URI Rewriting in Location Block

location /route/ {
    rewrite ^/route/(.*) /$1 break;
    proxy_pass http://127.0.0.1:9000;
}

2. Proxy Pass with Trailing Slash

location /route/ {
    proxy_pass http://127.0.0.1:9000/;
}

Note the crucial trailing slash after both the location and proxy_pass URLs.

3. Advanced Regex Matching

location ~ ^/route/(?.*)$ {
    proxy_pass http://127.0.0.1:9000/$path;
}

When implementing these solutions:

  • Always include the trailing slash in both location and proxy_pass for consistency
  • Test with various path depths to ensure proper rewriting
  • Consider adding proxy headers for better debugging:
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Here's a complete working configuration for a Node.js application:

server {
    listen 80;
    server_name localhost;

    location /api/ {
        proxy_pass http://127.0.0.1:3000/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

When working with Nginx's proxy_pass directive, a common requirement is to forward requests while stripping a specific path prefix. The default behavior preserves the original URI path, which isn't always what we want.

With this configuration:

location /route {
    proxy_pass http://127.0.0.1:9000;
}

A request to http://localhost/route/abc gets forwarded to http://localhost:9000/route/abc, maintaining the /route prefix.

To remove the /route prefix before forwarding, use one of these approaches:

Method 1: Regex Location with Capture Group

location ~ ^/route(/.*)$ {
    proxy_pass http://127.0.0.1:9000$1;
}

Method 2: Rewrite Directive

location /route {
    rewrite ^/route(.*)$ $1 break;
    proxy_pass http://127.0.0.1:9000;
}

Here's a full server block demonstrating the solution:

server {
    listen 80;
    server_name localhost;

    # Option 1: Regex location
    location ~ ^/route(/.*)$ {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_pass http://127.0.0.1:9000$1;
    }

    # Option 2: Rewrite + prefix location
    location /alt-route/ {
        rewrite ^/alt-route(/.*)$ $1 break;
        proxy_pass http://127.0.0.1:9000;
    }
}

When implementing this solution:

  • Always include proxy_set_header Host $host for proper header forwarding
  • Use the break flag with rewrite to prevent further rewrites
  • Test with trailing slashes in both original and target paths
  • Consider adding proxy_redirect if the backend generates redirects

Both methods automatically preserve query strings. For example:

# Request: /route/abc?param=value
# Becomes: http://127.0.0.1:9000/abc?param=value