When setting up a reverse proxy where you need to serve content from example.com
under example.net/bbb/
, several URL handling challenges emerge:
# Current problematic config snippet
location /bbb/ {
proxy_pass http://example.com/;
proxy_set_header Host $host;
# Missing critical headers for proper URL resolution
}
To properly handle both HTML content and static assets, we need multiple adjustments:
location /bbb/ {
proxy_pass http://example.com/;
proxy_set_header Host example.com; # Critical change
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;
# Rewrite response headers containing URLs
proxy_redirect ~^/(.*)$ /bbb/$1;
# Handle redirects from backend
proxy_intercept_errors on;
recursive_error_pages on;
error_page 301 302 307 = @handle_redirects;
}
location @handle_redirects {
set $saved_redirect_location '$upstream_http_location';
if ($saved_redirect_location ~ ^/(.*)$) {
return 301 /bbb/$1;
}
proxy_pass $saved_redirect_location;
}
For complete URL resolution, modify the served HTML (if possible):
<!-- In your application's head section -->
<base href="/bbb/" />
When you can't modify backend HTML, use Nginx sub_filter:
location /bbb/ {
# ... previous proxy settings ...
sub_filter 'src="/' 'src="/bbb/';
sub_filter 'href="/' 'href="/bbb/';
sub_filter_types text/html text/css application/javascript;
sub_filter_once off;
}
Here's a complete solution combining all techniques:
server {
listen 80;
server_name example.net;
location /bbb/ {
proxy_pass http://example.com/;
proxy_set_header Host example.com;
proxy_set_header X-Real-IP $remote_addr;
# URL rewriting in responses
proxy_redirect ~^/(.*)$ /bbb/$1;
# Content rewriting
sub_filter '="/' '="/bbb/';
sub_filter '=/' '=/bbb/';
sub_filter_types *;
sub_filter_once off;
# Redirect handling
proxy_intercept_errors on;
recursive_error_pages on;
error_page 301 302 307 = @handle_redirect;
}
location @handle_redirect {
set $saved_location '$upstream_http_location';
if ($saved_location ~ ^/(.*)$) {
return 301 /bbb/$1;
}
proxy_pass $saved_location;
}
}
Verify with these curl commands:
# Test HTML content
curl -I http://example.net/bbb/
# Test asset loading
curl -I http://example.net/bbb/css/main.css
# Test redirect handling
curl -v http://example.net/bbb/old-path
When configuring Nginx to reverse proxy from a subdirectory (like /bbb
) to a different domain's root, several URL resolution issues commonly occur:
location /bbb/ {
proxy_pass http://example.com/;
# Standard headers aren't enough
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
The primary issues stem from how browsers and servers interpret relative URLs differently:
- Relative paths in HTML/CSS/JS resolve against the browser's current URL
- Proxy servers don't automatically rewrite these paths
- The trailing slash in proxy_pass affects path processing
Here's the complete working configuration with all necessary components:
server {
listen 80;
server_name example.net;
root /path/to/aaa;
location /bbb/ {
proxy_pass http://example.com/; # Critical trailing slash
proxy_set_header Host example.com; # Original host, not $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;
# URL rewriting for HTML content
sub_filter_once off;
sub_filter 'href="/' 'href="/bbb/';
sub_filter 'src="/' 'src="/bbb/';
sub_filter 'url(/' 'url(/bbb/';
# Handle redirects from backend
proxy_redirect http://example.com/ /bbb/;
proxy_redirect / /bbb/;
}
# Static assets handling
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|ttf|woff|woff2)$ {
expires max;
access_log off;
}
}
1. Proper Host Header
Set the original host (not the proxy host) to maintain application functionality:
proxy_set_header Host example.com;
2. HTML Content Rewriting
The sub_filter directives handle path rewriting in HTML, CSS, and JavaScript:
sub_filter 'href="/' 'href="/bbb/';
sub_filter 'src="/' 'src="/bbb/';
sub_filter 'url(/' 'url(/bbb/';
3. Redirect Handling
Backend redirects need to be rewritten to maintain the /bbb prefix:
proxy_redirect http://example.com/ /bbb/;
proxy_redirect / /bbb/;
For SPAs (Single Page Applications), you'll need additional configuration:
location /bbb/ {
# Existing proxy config...
# Handle HTML5 mode routing
error_page 404 =200 /bbb/index.html;
# API proxy exceptions
location /bbb/api/ {
proxy_pass http://example.com/api/;
proxy_set_header Host example.com;
}
}
Verify your configuration with these curl commands:
# Check HTML rewriting
curl -v http://example.net/bbb/
# Verify asset paths
curl -v http://example.net/bbb/main.css
# Test API routing
curl -v http://example.net/bbb/api/status
Remember to test with different types of relative URLs including:
- Root-relative (/assets/image.png)
- Protocol-relative (//example.com/style.css)
- Path-relative (../parent/file.js)