How to Configure Nginx Reverse Proxy for Multiple Backend Applications with Path-Based Routing


2 views

When setting up an Nginx reverse proxy for multiple backend applications, a common challenge occurs when the backend applications generate absolute URLs without awareness of their proxy context path. This manifests when requests like https://proxyip/app1 get redirected to https://proxyip/Account/login instead of maintaining the /app1 prefix.

Here's a working configuration that handles path rewriting and header modifications:

server {
    listen 443 ssl;
    server_name proxy.example.com;

    ssl_certificate     /etc/nginx/cert.crt;
    ssl_certificate_key /etc/nginx/cert.key;

    # Standard proxy headers
    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;
    proxy_set_header    X-Forwarded-Prefix      /app1;  # For apps that support this

    # Important for path manipulation
    proxy_redirect     off;
    proxy_cookie_path  / /app1/;  # Rewrites Set-Cookie headers

    # Handle static files and assets
    location ~* ^/app1/(.+\.(js|css|png|jpg|jpeg|gif|ico|woff|ttf|svg))$ {
        proxy_pass https://10.10.0.1/$1;
    }

    # Main application routing
    location /app1/ {
        # Rewrite the path before passing to backend
        rewrite ^/app1/(.*)$ /$1 break;
        
        proxy_pass https://10.10.0.1/;
        
        # Handle redirects from backend
        proxy_redirect ~^(https?://[^/]+)/(.*)$ /app1/$2;
    }

    location /app2/ {
        rewrite ^/app2/(.*)$ /$1 break;
        proxy_pass https://10.10.0.2/;
        proxy_cookie_path / /app2/;
        proxy_redirect ~^(https?://[^/]+)/(.*)$ /app2/$2;
    }
}

1. Path Rewriting: The rewrite directive removes the /app1 prefix before passing to backend

2. Cookie Path Adjustment: proxy_cookie_path rewrites cookie paths to maintain session state

3. Redirect Handling: proxy_redirect ensures backend redirects maintain the proxy context

For applications that generate absolute URLs in HTML responses (common with ASP.NET), you may need additional measures:

# For applications that embed absolute URLs
sub_filter_once off;
sub_filter_types text/html text/css text/xml;
sub_filter 'href="/' 'href="/app1/';
sub_filter 'src="/' 'src="/app1/';
sub_filter 'url(/' 'url(/app1/';

When troubleshooting:

  • Check both Nginx access/error logs and backend application logs
  • Use curl -v to examine redirect behavior
  • Verify cookie paths with browser developer tools
  • Test with simple endpoints before complex applications

When setting up an Nginx reverse proxy for multiple backend applications, developers often encounter redirection issues where the application loses the original path context. This manifests when applications generate absolute URLs without considering the proxy prefix.

From the logs, we can observe a clear pattern:

GET /app1 → 301 → GET /app1/ → 302 → GET /Account/Login → 404

The backend application (likely ASP.NET MVC based on IIS logs) attempts to redirect to root-level paths instead of maintaining the /app1/ prefix.

Here's the proper Nginx configuration that handles both the proxy routing and URL rewriting:

server {
    listen 443 ssl;
    server_name proxy.example.com;

    ssl_certificate     /etc/nginx/cert.crt;
    ssl_certificate_key /etc/nginx/cert.key;

    # Standard proxy headers
    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;

    # Important for handling chunked responses
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    # Handle large file uploads
    client_max_body_size 100M;

    # Location for app1
    location ~ ^/app1(/?)(.*)$ {
        proxy_pass https://10.10.0.1/$2$is_args$args;
        proxy_redirect https://10.10.0.1/ /app1/;
        proxy_cookie_path / /app1/;
        proxy_cookie_domain 10.10.0.1 $host;
    }

    # Location for app2
    location ~ ^/app2(/?)(.*)$ {
        proxy_pass https://10.10.0.2/$2$is_args$args;
        proxy_redirect https://10.10.0.2/ /app2/;
        proxy_cookie_path / /app2/;
        proxy_cookie_domain 10.10.0.2 $host;
    }

    # Security: deny hidden files
    location ~ /\. {
        deny all;
    }
}

1. Path Preservation: The regex pattern ^/app1(/?)(.*)$ captures both with and without trailing slashes, and preserves the remaining path via $2.

2. Redirect Rewriting: proxy_redirect ensures backend-generated Location headers get rewritten to include the prefix.

3. Cookie Path Adjustment: proxy_cookie_path modifies Set-Cookie headers from the backend to maintain session consistency.

For ASP.NET applications specifically, you may need to configure the application to be aware of running behind a proxy:

# In Web.config for IIS-hosted apps:
<system.webServer>
    <rewrite>
        <rules>
            <rule name="ReverseProxyInbound" stopProcessing="true">
                <match url="(.*)" />
                <action type="Rewrite" url="{R:1}" />
                <serverVariables>
                    <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                    <set name="HTTP_ACCEPT_ENCODING" value="" />
                </serverVariables>
            </rule>
        </rules>
        <outboundRules>
            <rule name="ReverseProxyOutbound" preCondition="IsHtml">
                <match filterByTags="A, Form, Img" pattern="^/(.*)" />
                <action type="Rewrite" value="/app1/{R:1}" />
            </rule>
            <preConditions>
                <preCondition name="IsHtml">
                    <add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
                </preCondition>
            </preConditions>
        </outboundRules>
    </rewrite>
</system.webServer>

When troubleshooting, examine these key elements:

1. Check both Nginx access/error logs
2. Inspect backend application logs
3. Use curl with -v flag to examine headers:
   curl -vk https://proxyip/app1
4. Verify cookie paths in browser developer tools
5. Check for hardcoded URLs in frontend JavaScript

For applications using WebSockets, add these directives:

location /app1/ws/ {
    proxy_pass https://10.10.0.1/ws/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400;
}