Proxy pass

Proxy pass#

In my experience, proxying requests is the most common use of the location directive, making the proxy_pass directive crucial in this context. This page explores various options associated with the proxy_pass directive.

Check corresponding official documentation section.

This section features a slightly complex setup to minimize code duplication during experiments. It uses its own Docker container, created in the following cell.

docker run --rm -it --name nginx_proxy_pass -p 80:80 -itd nginx
691884df1963414a323026ed9b8d3a001fb09f4bffc9118eecf2c21f3be7cd67

It will have the main server listening on port 80, and a mirror server on port 81 that simply returns the URL it was requested with. This setup makes it easy to monitor how different configurations transform requests generated by NGINX in various cases.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/nginx.conf' <<EOF
events {}
http {

    server {
        listen 80;
        include conf.d/default.conf;
    }

    server {
        listen 81;
        location / {return 200  "\$scheme://\$host\$request_uri";}
    }
}
EOF

The previous configuration referred to the conf.d/default.conf file, where you can experiment with various proxy_pass configurations. The following cell sets up the proxy on port 80 to forward requests to the mirror server.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/conf.d/default.conf' <<EOF
location / {
    proxy_pass "http://localhost:81";
}
EOF
docker exec -i nginx_proxy_pass nginx -s reload &> /dev/null

Note: We cannot use proxy_pass without specifying location in a modifiable configuration. The configuration and context of the location directive can affect the behavior of proxy_pass.

Now, let’s verify how it functions:

curl localhost:80/test
http://localhost/test

Referring to the /test uri on localhost returns http://localhost/test - expected behavior.

Don’t forget to clean up the environment after everything is done.

docker stop nginx_proxy_pass
nginx_proxy_pass

URI to upstream#

This section reviews the rules in Nginx for handling request passes. The focus is on how the portion of the pass matching the value specified in location interacts with the final segment of the pass - determining how it is appended to the URL defined in proxy_pass.

There are such rules:

  • If only host is given as an argument to proxy_pass: it will add the entire pass of the original request (including part of the pass specified in the `location’ directive).

  • If there are host with pass (including case when there only empty slash / after host) it will add only that part of the original request that doesn’t match pass specified in location.


Consider some examples.

The following cell specifies proxy_pass just with host name.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/conf.d/default.conf' <<EOF
location /my_pass/ {
    proxy_pass "http://localhost:81";
}
EOF
docker exec -i nginx_proxy_pass nginx -s reload &> /dev/null
sleep 1
curl localhost:80/my_pass/test
http://localhost/my_pass/test

So the full pass of the original request is added to the proxy URL.

The following example adds extra_pass to the host specified in the proxy_pass directive.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/conf.d/default.conf' <<EOF
location /my_pass/ {
    proxy_pass "http://localhost:81/extra_pass/";
}
EOF
docker exec -i nginx_proxy_pass nginx -s reload &> /dev/null
sleep 1

So the proxied request would just add the last part of the original request.

curl localhost:80/my_pass/hello/test
http://localhost/extra_pass/hello/test

An important particular case of the previous example is when you want to use only a specific part of the incoming request by specifying an empty path that contains just /.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/conf.d/default.conf' <<EOF
location /my_pass/ {
    proxy_pass "http://localhost:81/";
}
EOF
docker exec -i nginx_proxy_pass nginx -s reload &> /dev/null
sleep 1
curl localhost:80/my_pass/test
http://localhost/test

Note: The exact part of the original request that does not match the value specified in the location directive will be used. The following cell shows examples where this can lead to slash duplication. Since no slash is specified in the location directive, it will be taken from the incoming request, but a slash is already included in the proxy_pass directive.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/conf.d/default.conf' <<EOF
location /my_pass {
    proxy_pass "http://localhost:81/";
}
EOF
docker exec -i nginx_proxy_pass nginx -s reload &> /dev/null
sleep 1
curl localhost:80/my_pass/test
http://localhost//test

Wrong pass#

Note that if you try to specify a non-existent pass in the proxy_pass directive, it will lead to an error during configuration validation.


The following cell defines new nginx config that uses url http://this_pass_doesnt_exist as proxy_pass which is obviously wrong.

docker exec -i nginx_proxy_pass sh -c 'cat >  /etc/nginx/conf.d/default.conf' <<EOF
location / {
    proxy_pass "http://this_pass_doesnt_exist";
}
EOF
docker exec -i nginx_proxy_pass nginx -t | true
2024/12/13 12:25:03 [emerg] 182#182: host not found in upstream "this_pass_doesnt_exist" in /etc/nginx/conf.d/default.conf:2
nginx: [emerg] host not found in upstream "this_pass_doesnt_exist" in /etc/nginx/conf.d/default.conf:2
nginx: configuration file /etc/nginx/nginx.conf test failed

During the validation of this config, we receive an error indicating that the specified host does not exist. This issue can cause problems when starting the container.