如何将 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_pass
和Host
标头集禁用proxy_redirect
,这可能是您的麻烦之源!
这对于处理 POST
和 GET
请求非常有效,尽管使用的是完全不同的网络服务器。
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/... 重新路由到内部服务?的主要内容,如果未能解决你的问题,请参考以下文章
Tastypie ManyToMany 中的 POST / PUT 错误
在 post、put 和 delete 上使用 mongoose 和 node.js 休息 api
Chrome 中的 AJAX 发送选项而不是 GET/POST/PUT/DELETE?