如何将 Nginx 中的 POST/PUT/... 重新路由到内部服务?

Posted

技术标签:

【中文标题】如何将 Nginx 中的 POST/PUT/... 重新路由到内部服务?【英文标题】:How to reroute POST/PUT/... in Nginx to internal service? 【发布时间】:2021-01-28 17:52:24 【问题描述】:

我有一个安装了 nginx 的服务器(ubuntu 20.04 核心)。在同一台服务器上,我在端口 8080 上有一个正在运行的 Spring Boot Web(2.3.3.RELEASE) 服务。

我想从外部访问 Spring Boot Web 服务的资源 例如http://server/api/users 访问 http://localhost/api/users 并将响应返回给客户端。

我在 nginx 中配置了以下规则:

location /api 
    proxy_pass http://localhost:8080;

这适用于 GET 请求,但不适用于 POST 或 PUT。对客户端的第一个响应是状态码为 301 的响应:已永久移动,根据HTTP spec,客户端应将 HTTP 方法更改为 GET 或 HEAD。客户端在第一次响应后自动更改方法,如 /var/log/nginx/access.log 中的 nginx 日志文件中所示:

192.168.0.1 - - [14/Oct/2020:11:23:56 +0100] "POST /api/users?key=key HTTP/1.1" 301 162 "-" "PostmanRuntime/7.26.5"
192.168.0.1 - - [14/Oct/2020:11:23:56 +0100] "GET /api/users?key=key HTTP/1.1" 200 93 "http://server/api/users?key=key" "PostmanRuntime/7.26.5"
192.168.0.1 - - [14/Oct/2020:11:23:59 +0100] "PUT /api/users/1/deleted?key=key&deleted=false HTTP/1.1" 301 162 "-" "PostmanRuntime/7.26.5"
192.168.0.1 - - [14/Oct/2020:11:23:59 +0100] "GET /api/users/1/deleted?key=key&deleted=false HTTP/1.1" 405 141 "http://server/api/users/1/deleted?key=key&deleted=false" "PostmanRuntime/7.26.5"

来自 PUT-Request 的 GET-Request 失败,出现 405,因为路径“/api/users/id/deleted”没有映射的 GET。我尝试在“location /api ..”中添加和修改许多不同的配置,例如:

location /api 
  proxy_pass      http://localhost:8080;
  proxy_redirect  http://localhost:8080/ /; # I tried "../api /", ".../api/ /", ".../ /api" 
  proxy_read_timeout 60s;

  proxy_set_header          Host            $host;
  proxy_set_header          X-Real-IP       $remote_addr;
  proxy_set_header          X-Forwarded-For $proxy_add_x_forwarded_for;

建议在serverfault(how do I get nginx to forward HTTP POST requests via rewrite?)

我在trac.nginx.com 上发现了完全相同的问题,但该配置也不起作用:

location /api  # "/api" and "/api/" doesn't work
    proxy_pass ​http://localhost:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;

我目前对如何让它发挥作用感到很迷茫。

--- 编辑:在@ti7 和@ampularius 的帮助下,现在的工作解决方案是:

NGINX 位置条目:

location /api 
    proxy_pass http://localhost:8080;
    proxy_redirect off;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Port  $server_port;

并且在 Spring Boot 服务的 application.properties 中没有进行任何更改,因此这些设置中的 NONE 对这种情况有任何作用:

server.use-forward-headers=true
server.forward-headers-strategy=native
server.forward-headers-strategy=framework
server.forward-headers-strategy=none

【问题讨论】:

【参考方案1】:

最近我一直在与优秀的Gunicorn WSGI server 合作,使用 nginx 前端。

deploy docs 建议使用proxy_passHost 标头集禁用proxy_redirect,这可能是您的麻烦之源!

这对于处理 POSTGET 请求非常有效,尽管使用的是完全不同的网络服务器。

location @proxy_to_app 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    # we don't want nginx trying to do something clever with
    # redirects, we set the Host: header above already.
    proxy_redirect off;
    proxy_pass http://app_server;

【讨论】:

所以你的建议是在一台机器上使用两个网络服务器? @Blauspecht 抱歉,不 - 我建议您禁用 proxy_redirect! 哇,这和 ampularius 的设置都起作用了。非常感谢你们!所以现在位置条目看起来像这样location /api proxy_pass http://localhost:8080; proxy_redirect off; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Server $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; @Blauspecht 太棒了! 1)创建一个新的答案来显示您的最终代码或 2)将其编辑到您的问题中的新代码块中(--- 编辑这两部分成为我的最终代码: ) 让以后的读者更清楚! 是的,我也是这么想的,我想拆分它,但那是不可能的。谢谢你给@ampularius他的份额!【参考方案2】:

这可能是因为您的后端服务器认为请求是 http 并将其重定向到 https。尝试将其添加到您的位置块中:

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port  $server_port;

它让您的后端服务器知道该请求实际上是通过 https 发出的,但 tls 会进一步终止(在大多数情况下,您必须将代理服务器列入白名单才能使其正常工作)。

根据this answer,您可以在 application.properties 中这样做:

server.use-forward-headers=true

【讨论】:

遗憾的是,事实并非如此,至少我是这么认为的:我添加了“server.forward-headers-strategy=NATIVE”,因为不推荐使用“server.use-forward-headers”并且 strategy=NATIVE 应该这样做与this answer相同。 @Blauspecht 在这种情况下,您应该能够只添加 X-Forwarded-Proto 和 X-Forwarded-Port。至少我是这么认为的(假设您使用 https 连接到代理)。 更新:我用“server.forward-headers-strategy=framework”和你的建议“server.use-forward-headers=true”试过了,没有改变。你提到了https,我在nginx中启用了https并且从http重新路由到https,这和它有什么关系吗? 但是您是否将我建议的两个 proxy_set_header 行添加到 nginx 配置中?它们很重要。 这是我做的第一件事,是的。现在在位置 /api ... 中有 proxy_pass 和两个 proxy_set_header 行。逻辑上我也重启了 nginx。

以上是关于如何将 Nginx 中的 POST/PUT/... 重新路由到内部服务?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 nginx 上配置 cors?

Tastypie ManyToMany 中的 POST / PUT 错误

在 post、put 和 delete 上使用 mongoose 和 node.js 休息 api

Chrome 中的 AJAX 发送选项而不是 GET/POST/PUT/DELETE?

如何使用 post/put WCF RestFul Service

HTTP提交方式之PUT详细介绍及POST和PUT的区别