Nginx Reverse Proxy Configuration: Preserve Original URL with Path Rewriting


2 views

When setting up Nginx as a reverse proxy, one common requirement is to redirect requests while keeping the original URL visible in the browser. The specific scenario we're addressing involves:

http://example.com/some/path -> http://192.168.1.24

with subsequent requests like:

http://example.com/some/path/section/index.html -> http://192.168.1.24/section/index.html

The initial attempt using this configuration has two main issues:

server {
    listen          80;
    server_name     www.example.com;

    location /some/path/ {
        proxy_pass http://192.168.1.24;
        proxy_redirect http://www.example.com/some/path http://192.168.1.24;
        proxy_set_header Host $host;
    }

    location / {
        index index.html;
        root  /var/www/example.com/htdocs;
    }
}

1. The backend server receives requests with /some/path included
2. Links on served pages lose the /some/path prefix

Here's the working configuration that solves both problems:

server {
    listen 80;
    server_name www.example.com;

    location /some/path/ {
        proxy_pass http://192.168.1.24/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Handle redirects properly
        proxy_redirect ~^http://192.168.1.24/(.*)$ http://$host/some/path/$1;
        
        # Rewrite links in HTML responses
        sub_filter_once off;
        sub_filter 'href="/' 'href="/some/path/';
        sub_filter 'src="/' 'src="/some/path/';
    }

    location / {
        index index.html;
        root /var/www/example.com/htdocs;
    }
}

1. The trailing slash in proxy_pass:
proxy_pass http://192.168.1.24/ ensures the /some/path is stripped before reaching the backend.

2. Advanced proxy_redirect:
The regex pattern handles all possible redirects from the backend while preserving the original URL structure.

3. HTML content rewriting:
The sub_filter directives modify links in the HTML response to maintain the /some/path prefix.

After implementing this configuration:

  1. Restart Nginx: sudo systemctl restart nginx
  2. Test with curl: curl -v http://example.com/some/path/
  3. Verify the response headers and HTML content

For applications that use absolute URLs in JavaScript or CSS, you might need additional filters:

sub_filter 'url(/' 'url(/some/path/';
sub_filter '"/api/' '"/some/path/api/';

For WebSocket connections, add:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

When implementing reverse proxy configurations in Nginx, a common requirement is to maintain the original URL structure in the browser while routing requests to different backend servers. Our specific scenario involves:

  • Original URL: http://example.com/some/path
  • Backend server: http://192.168.1.24
  • Need to preserve /some/path in browser address bar
  • Properly rewrite URLs in served content

The current configuration has two main problems:

1. Backend receives requests with /some/path included
2. Relative links in served pages don't maintain /some/path

Here's the working configuration that addresses both issues:

server {
    listen 80;
    server_name www.example.com;

    location /some/path/ {
        proxy_pass http://192.168.1.24/;
        proxy_redirect ~^/(.*)$ /some/path/$1;
        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;
        
        # Handle redirects from backend
        proxy_redirect http://192.168.1.24/ /some/path/;
        proxy_redirect ~^(http://192.168.1.24)(/.+)$ /some/path$2;
    }

    location / {
        index index.html;
        root /var/www/example.com/htdocs;
    }
}

1. The trailing slash in proxy_pass:
proxy_pass http://192.168.1.24/ (with trailing slash) strips the /some/path from the backend request.

2. Comprehensive proxy_redirect rules:
The regex patterns handle all redirect cases from the backend server while maintaining the original URL structure.

3. Header preservation:
Essential headers ensure the backend server receives proper client information.

To verify the configuration works:

# Original request:
curl -I http://example.com/some/path

# Should return content from 192.168.1.24 while showing:
# Location headers rewritten to maintain /some/path

# Test link navigation:
curl http://example.com/some/path/section/index.html
# Should properly serve content from 192.168.1.24/section/index.html

For complex setups with multiple backend servers:

map $request_uri $backend {
    ~^/some/path1/ 192.168.1.24;
    ~^/some/path2/ 192.168.1.25;
    # Add more mappings as needed
}

server {
    # ... other config ...
    
    location ~ ^/(some/path\d)/ {
        set $path_prefix $1;
        proxy_pass http://$backend/;
        proxy_redirect ~^/(.*)$ /$path_prefix/$1;
        # ... other proxy settings ...
    }
}

This dynamic approach scales better for managing hundreds of backend servers while maintaining clean URL structures.