当主机端口!=容器端口时,Nginx 在 docker 机器上重写

当主机端口!=容器端口时,Nginx 在 docker 机器上重写

我正在尝试运行多个 docker 容器,所有容器都运行 nginx 并监听端口 80,但不同的主机端口映射到容器端口 80。

在大多数情况下,这种方法都是有效的,除非 nginx 因缺少尾随斜杠而进行重定向。

server {
    listen 80;
    root /var/www;
    index index.html;
    location /docs {}
}

鉴于上述 nginx 配置和运行它的 docker 容器,其中主机端口 8080 映射到容器端口 80,我可以通过 curl ok 获取 localhost:8080/docs/:

> GET /docs/ HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
* Server nginx/1.9.5 is not blacklisted
< Server: nginx/1.9.5
< Date: Sat, 28 Nov 2015 17:27:05 GMT
< Content-Type: text/html
< Content-Length: 6431
< Last-Modified: Sat, 28 Nov 2015 17:17:06 GMT
< Connection: keep-alive
< ETag: "5659e192-191f"
< Accept-Ranges: bytes
<
... html page ...

但如果我请求 localhost:8080/docs 我会重定向到 localhost/docs/

> GET /docs HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
* Server nginx/1.9.5 is not blacklisted
< Server: nginx/1.9.5
< Date: Sat, 28 Nov 2015 17:29:40 GMT
< Content-Type: text/html
< Content-Length: 184
< Location: http://localhost/docs/
< Connection: keep-alive
<
... html redirect page ...

如何让 nginx 在重定向时保留原始端口?我尝试查看 port_in_redirect 和 server_name_in_redirect,但它们没有帮助。


编辑

基于https://forum.nginx.org/read.php?2,261216,261216#msg-261216目前看来这还不可能。

答案1

最简单的解决方案是删除该index指令,并且不依赖显式或隐式$uri/重定向。例如:

server {
  listen 80;
  root /var/www;
  location /docs {
    try_files $uri $uri/index.html =404;
  }
}

这不是完全相同的行为,因为它完全避免了重定向。如果您想要像 index 模块提供的尾部斜杠重定向,则需要更复杂的解决方案。例如:

server {
  listen 80;
  root /var/www;
  location /docs {
    try_files $uri @redirect;
  }
  location @redirect {
    if ($uri ~* ^(.+)/$) { rewrite ^ $uri/index.html last; }
    if (-d $document_root$uri) { return $scheme://$host:8080$uri/; }
    return 404;
  }
}

答案2

HTTP 客户端会将端口放入 Host 标头中。如果在执行重定向时使用主机标头的原始值,它应该可以按预期工作。我测试了以下代码,看起来完全符合您的要求:

location ~ ^.*[^/]$ {
    try_files $uri @rewrite;
}
location @rewrite {
    return 302 $scheme://$http_host$uri/;
}

> GET /bla HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.9.7
< Date: Sun, 29 Nov 2015 06:23:35 GMT
< Content-Type: text/html
< Content-Length: 160
< Connection: keep-alive
< Location: http://localhost:8080/bla/

答案3

只需按照这个简单的修复方法

location /app {
    alias /usr/share/nginx/html/folder;
    if (-d $request_filename) {
        rewrite [^/]$ $scheme://$http_host$uri/ permanent;
    }
}

答案4

我认为这是因为你的 Docker 是 nginx 容器的 NAT/Proxy。对于不同端口之间的路由,需要添加信息。

对于我来说,这个问题已经解决了,将以下内容添加到 nginx.conf

http {
    ...
    proxy_set_header Host $http_host; # required for docker client's sake
    proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    ...
}

相关内容