Issue with Nginx Location / Adding or Omitting /

Recently, when configuring Nginx, I have been confused about how to configure Location / and how to access and configure it correctly. I always have to ponder over it, so this time I decided to set up a backend to receive the Nginx reverse proxy and output the actual request path.(English version Translated by GPT-3.5, 返回中文)

Recently, I have encountered many places where I use Nginx in my projects. But the issue with this location /xxx in Nginx has been a headache. Although there is plenty of information available online, it’s not always easy to find the most suitable solution for oneself. So, I decided to write my own explanation.

The official Nginx website also provides detailed explanations Module ngx_http_proxy_module - nginx.org. From now on, the term “above” refers to the location section, and “below” refers to the proxy_pass section.

Regular Requests

This request doesn’t require any special configuration. It simply forwards the request to the corresponding location.

1
2
3
location / {
proxy_pass http://127.0.0.1:8000;
}

Test 1

1
2
请求:http://127.0.0.1:9999/path1/path2/path3/maven2/io/netty/netty.jar
实际地址:http://127.0.0.1:8000/path1/path2/path3/maven2/io/netty/netty.jar

Including this configuration is also the same.

1
2
3
location /test {
proxy_pass http://127.0.0.1:8000;
}

Test 2

1
2
请求:http://127.0.0.1:9999/test/netty.jar
实际地址:http://127.0.0.1:8000/test/netty.jar

According to the official documentation, this statement means

If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, or the full normalized request URI is passed when processing the changed URI.

In other words, if there is no URI defined in proxy_pass, the entire request address will be passed to the backend. So, if you want to forward /api to the backend /api, this configuration would suffice.

1
2
3
location /api {
proxy_pass http://127.0.0.1:8000;
}

Both Sides with /

In this case, the request will remove /test/ and directly request the URL after it to the backend, as shown below.

1
2
3
4
5
6
location /test/ {
proxy_pass http://127.0.0.1:8000/;
}

请求:http://127.0.0.1:9999/test/netty.jar
实际地址:http://127.0.0.1:8000/netty.jar

According to the official documentation:

If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive.

If proxy_pass is specified with a URI, then the normalized request address matching the location will be replaced by the URL specified in the directive when it is forwarded to the backend. Well, my Chinese language skills are not good, but anyway, it’s best to try a few different configurations.

Test 1 - Front Short, Back Long

1
2
3
4
5
6
location /test/ {
proxy_pass http://127.0.0.1:8000/central/maven2/;
}

请求:http://127.0.0.1:9999/test/io/netty.jar
实际地址:http://127.0.0.1:8000/central/maven2/io/netty.jar

Test 2 - Front Long, Back Short

1
2
3
4
5
location /private/maven-repo/maven2/ {
proxy_pass http://127.0.0.1:8000/maven-repo/;
}
请求:http://127.0.0.1:9999/private/maven-repo/maven2/io/netty.jar
实际地址:http://127.0.0.1:8000/maven-repo/io/netty.jar

Test 3 - Front Long, Back Slightly Long

1
2
3
4
5
location /private/maven-repo/maven2/ {
proxy_pass http://127.0.0.1:8000/maven-repo/central/;
}
请求:http://127.0.0.1:9999/private/maven-repo/maven2/io/netty.jar
实际地址:http://127.0.0.1:8000/maven-repo/central/io/netty.jar

Conclusion

From the above two tests, it can be inferred that the directive mentioned refers to the URL in the proxy_pass section below. For requests with / on both sides, the /xxxxx/yyy/ in location /xxxxx/yyy/ will be completely replaced by the URL after proxy_pass, including all subsequent paths. For example, when location /private/maven-repo/maven2/ is forwarded to http://127.0.0.1:8000/maven-repo/, /private/maven-repo/ will be replaced with /maven-repo/ before being sent to the backend.

Path Included with / on Top and None on Bottom

Contrary to regular requests, here ‘none on bottom’ refers to the case where a path is included. Based on the previous test, we can conclude as follows:

1
2
3
4
5
6
7
8
9
10
11
location /private/maven-repo/maven2/ {
proxy_pass http://127.0.0.1:8000/maven-repo/central;
}

请求:http://127.0.0.1:9999/private/maven-repo/maven2/io/netty.jar
实际地址:http://127.0.0.1:8000/maven-repo/centralio/netty.jar
注意上面的central和io合在一起了

下面的请求,maven2和io之间用了2个/
请求:http://127.0.0.1:9999/private/maven-repo/maven2//io/netty.jar
实际地址:http://127.0.0.1:8000/maven-repo/centralio/netty.jar

Based on the logic above, /private/maven-repo/maven2/ will be replaced directly with /maven-repo/central and forwarded to the backend. In this case, the concatenated address may be combined.

Path Included, None on Top, / on Bottom, and None on Both Sides

This can also be done following the logic mentioned above.

Test 1 - None on Top, / on Bottom

1
2
3
4
5
6
location /private/maven-repo/maven2 {
proxy_pass http://127.0.0.1:8000/maven-repo/central/;
}
请求:http://127.0.0.1:9999/private/maven-repo/maven2/io/netty.jar
实际地址:http://127.0.0.1:8000/maven-repo/central//io/netty.jar
注意上面的地址,central和io之间有2个 /

Test 2 - None on Both Sides

1
2
3
4
5
location /private/maven-repo/maven2 {
proxy_pass http://127.0.0.1:8000/maven-repo/central;
}
请求:http://127.0.0.1:9999/private/maven-repo/maven2/io/netty.jar
实际地址:http://127.0.0.1:8000/maven-repo/central/io/netty.jar

It turns out to be a replacement process as well. For the case with none on top and / on the bottom, /private/maven-repo/maven2 will be replaced with /maven-repo/central/ and the subsequent parameters will be added directly, resulting in /maven-repo/central//io/netty.jar. For the case with none on both sides, /private/maven-repo/maven2 will be replaced with /maven-repo/central.

Therefore, if there is only one / in the configuration, it will request like this.

1
2
3
4
5
6
7
8
location / {
proxy_pass http://127.0.0.1:8000/central/repo;
}

请求:http://127.0.0.1:9999/maven2/io/netty.jar
将 / 替换成 /central/repo,然后加上后面的maven2/io/netty.jar
后端地址:http://127.0.0.1:8000/central/repomaven2/io/netty.jar
注意上面repo和maven2合并了

Summary

In the aforementioned tests, there are basically two situations: direct address proxying and automatic replacement.

  1. When the proxy_pass is filled with only the Host and there are no additional paths, it will forward the request in the same form it receives.

    1
    2
    location /aa/bb ->  请求 /aa/bb/cc.jar
    后端地址:/aa/bb/cc.jar
  2. If any Host is added to the proxy_pass and there are no additional parameters, the entire content after the location will be replaced with the URL specified in the proxy_pass directive.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    情况一
    location /jcenter/maven2/
    proxy_pass https://jcenter.bintray.com/
    会将请求中的 /jcenter/maven2/ 替换成 /
    例如 /jcenter/maven2/io/netty.jar 会请求到 https://jcenter.bintray.com/io/netty.jar

    情况二
    如果location 结尾没有 /,但是proxy_pass后面有 /,就会出现 // 的情况
    location /jcenter/maven2
    proxy_pass https://jcenter.bintray.com/
    请求:/jcenter/maven2/io/netty.jar
    首先将 /jcenter/maven2 替换成 /,然后拼接后面的内容,实际请求就变成了
    https://jcenter.bintray.com//io/netty.jar

    情况三
    这个情况对于location中结尾有/,proxy_pass没有 / 结果是一样的
    location /maven2/
    proxy_pass https://repo1.apache.org/maven2
    对于请求 /maven2/io/netty.jar
    按照规则将 /maven2/ 替换成/maven2, 然后拼接后面的字符,就变成了
    https://repo1.apache.org/maven2io/netty.jar

    情况四,与情况2类似,就是都是有 / 的情况下
    这个情况对于location中结尾有/,proxy_pass也有 / 结果和情况2是一样的
    location /maven2/
    proxy_pass https://repo1.apache.org/maven/maven2/
    对于请求 /maven2/io/netty.jar
    按照规则将 /maven2/ 替换成/maven/maven2/, 然后拼接后面的字符io/netty.jar,就变成了
    https://repo1.apache.org/maven/maven2/io/netty.jar
    但是这里如果请求 /maven2,就会出现自动加 / 的问题,这个自行查阅下吧,很容易理解的。(server_name_in_redirect以及port_in_redirect以及absolute_redirect,知道这3个,就知道怎么解决了。)