Mastering Nginx proxy_pass: When to Use / and How It Affects URLs
This article explains the nuances of Nginx's proxy_pass directive, illustrating how trailing slashes and URI paths affect backend request URLs, compares configurations across http and stream modules, and provides practical load‑balancing examples with detailed parameter settings.
Nginx is one of the most commonly used reverse‑proxy tools; the proxy_pass directive is powerful for interface proxying and load balancing, but the parameter after proxy_pass is subtle— even a trailing “/” can cause problems.
Examples of proxy_pass with or without trailing slash in a location block
When the URL in proxy_pass ends with “/”, the matched path from the location block is not appended to the backend URL; without “/”, the matched path is appended.
Assume the client accesses http://192.168.1.8/proxy/test.html and the following four configurations are used:
location /proxy/ {
proxy_pass http://127.0.0.1/;
}Resulting backend URL:
http://127.0.0.1/test.html location /proxy/ {
proxy_pass http://127.0.0.1;
}Resulting backend URL:
http://127.0.0.1/proxy/test.html location /proxy/ {
proxy_pass http://127.0.0.1/aaa/;
}Resulting backend URL:
http://127.0.0.1/aaa/test.html location /proxy/ {
proxy_pass http://127.0.0.1/aaa;
}Resulting backend URL: http://127.0.0.1/aaatest.html Summary: In a location block, if the URI after proxy_pass contains a trailing “/”, it represents an absolute root path; if it lacks the “/”, it is a relative path and the matched location part is appended.
Does not affect the URL shown in the browser address bar.
Sets the protocol and address of the upstream server.
Protocol can be http or https.
Address can be a domain name or IP.
Two Nginx modules that provide proxy_pass
ngx_http_proxy_module – syntax: proxy_pass URL; – usable in location, if, limit_except contexts.
ngx_stream_proxy_module – syntax: proxy_pass address; – usable in server context.
The http module’s proxy_pass requires a protocol, address, and optional URI, while the stream module’s proxy_pass only needs an address (domain/IP and port or a unix‑socket).
Specific usage of proxy_pass
ngx_stream_proxy_module configuration
server {
listen 127.0.0.1:12345;
proxy_pass 127.0.0.1:8080;
}
server {
listen 12345;
proxy_connect_timeout 1s;
proxy_timeout 1m;
proxy_pass example.com:12345;
}
server {
listen 53 udp;
proxy_responses 1;
proxy_timeout 20s;
proxy_pass dns.example.com:53;
}
server {
listen [::1]:12345;
proxy_pass unix:/tmp/stream.socket;
}ngx_http_proxy_module configuration
server {
listen 80;
server_name www.test.com;
# Normal proxy, no URL rewrite
location /some/path/ {
proxy_pass http://127.0.0.1;
}
# Proxy with trailing slash in backend URL
location /testb {
proxy_pass http://www.other.com:8801/;
}
# Conditional proxy using if
location /google {
if ($geoip_country_code ~ (RU|CN)) {
proxy_pass http://www.google.hk;
}
}
# Proxy with unix socket and limit_except
location /yongfu/ {
proxy_pass http://unix:/tmp/backend.socket:/uri/;
limit_except PUT DELETE {
proxy_pass http://127.0.0.1:9080;
}
}
}Analysis of backend request_uri after proxy_pass
# Scenario A – non‑regex location
location ^~ /testa/ {
proxy_pass http://127.0.0.1:8801;
}
# Request http://www.test.com/testa/aaaa → backend request_uri = /testa/aaaa
# Scenario B – non‑regex location with trailing slash
location ^~ /testb/ {
proxy_pass http://127.0.0.1:8801/;
}
# Request http://www.test.com/testb/bbbb → backend request_uri = /bbbb
# Scenario C – regex location (valid)
location ~ /testc {
proxy_pass http://127.0.0.1:8801;
}
# Scenario D – regex location (invalid, URI part not allowed)
location ~ /testd {
proxy_pass http://127.0.0.1:8801/; # error
}
# Scenario E – using $request_uri variable
location /ccc/ {
proxy_pass http://127.0.0.1:8801/aaa$request_uri;
}
# Scenario F – URL rewrite to pseudo‑static
location /namea/ {
rewrite /namea/([^/]+) /yongfu?namea=$1 break;
proxy_pass http://127.0.0.1:8801;
}
# Scenario G – rewrite with trailing slash (URI part ignored)
location /nameb/ {
rewrite /nameb/([^/]+) /yongfu?nameb=$1 break;
proxy_pass http://127.0.0.1:8801/;
}Key takeaways: Scenarios A and B show how a trailing slash in proxy_pass influences the backend request_uri; Scenario D demonstrates that regex locations cannot contain a URI part; Scenario E rewrites the request_uri using variables; Scenarios F and G use rewrite + break to achieve pseudo‑static URLs, noting that the URI part in proxy_pass is ignored in scenario G.
Load balancing practice
proxy_pass is required for load balancing configuration.
A single location can proxy to one server; load balancing proxies client requests to an upstream pool.
Load balancing configuration module (proxy_pass)
upstream game {
server 172.16.1.8:80;
server 172.16.1.7:80;
}
server {
server_name game1.test.com;
listen 80;
location / {
proxy_pass http://game;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
proxy_send_timeout 10s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
proxy_next_upstream http_404 http_500 http_502 http_503 http_504 http_403 http_429;
}
}Common proxy_pass parameters
Headers
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;Timeouts
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
proxy_send_timeout 10s;Buffering
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;Common parameter configuration
Method 1 – write parameters directly in http, server, or location blocks:
upstream game {
server 172.16.1.8:80;
server 172.16.1.7:80;
}
server {
server_name game1.test.com;
listen 80;
location / {
proxy_pass http://game;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
proxy_send_timeout 10s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
proxy_next_upstream http_404 http_500 http_502 http_503 http_504 http_403 http_429;
}
}Method 2 – place parameters in a separate file and include it, which simplifies maintenance.
# proxy_params
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 10s;
proxy_read_timeout 10s;
proxy_send_timeout 10s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
proxy_next_upstream http_404 http_500 http_502 http_503 http_504 http_403 http_429; # game.conf
upstream game {
server 172.16.1.8:80;
server 172.16.1.7:80;
}
server {
server_name game1.test.com;
listen 80;
location / {
proxy_pass http://game;
include proxy_params;
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Raymond Ops
Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
