Nginx https重写将POST转为GET

Nginx https重写将POST转为GET

我的代理服务器在 IP A 上运行,人们通过它访问我的 Web 服务。nginx 配置将重定向到 IP B 上的虚拟机。

对于 IP A 上的代理服务器,我在我的站点中提供了这个

server {
        listen 443;
        ssl on;
        ssl_certificate nginx.pem;
        ssl_certificate_key nginx.key;

        client_max_body_size 200M;
        server_name localhost 127.0.0.1;
        server_name_in_redirect off;

        location / {
                proxy_pass http://10.10.0.59:80;
                proxy_redirect http://10.10.0.59:80/ /;

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

}

server {
        listen 80;
        rewrite     ^(.*)   https://$http_host$1 permanent;
        server_name localhost 127.0.0.1;
        server_name_in_redirect off;
        location / {
                proxy_pass http://10.10.0.59:80;
                proxy_redirect http://10.10.0.59:80/ /;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

取自proxy_redirect如何让 nginx 通过重写转发 HTTP POST 请求?

由于重写,所有到达公网 IP 的请求都会到达 443。在内部,我们在虚拟机上转发到 80。

但是当我运行下面的python脚本来测试我们的配置时

import requests

data = {'username': '....', 'password': '.....'}
url = 'http://IP_A/api/service/signup'

res  = requests.post(url, data=data, verify=False)
print res
print res.json
print res.status_code
print res.headers

我得到了一个405 Method Not Allowed。在 nginx 中我们发现,当它到达内部服务器时,内部 nginx 正在获取一个GET请求,即使在原始标头中我们做了一个POST(这在 Python 脚本中显示)。

所以看起来 rewrite 有问题。有什么办法可以解决这个问题吗?当我注释掉 rewrite 时,它​​肯定会达到 80,并且通过了。由于 rewrite 能够与我们的内部服务器通信,因此 rewrite 本身没有问题。只是 rewrite 下降到POSTGET

谢谢你!

(Nginx 论坛上也会问到这个问题,因为这是一个关键的阻止程序......)

答案1

这不是 Nginx,而是你的浏览器。

RFC2616 的注释:

RFC 1945 和 RFC 2068 规定不允许客户端更改重定向请求的方法。但是,大多数现有的用户代理实现将 302 视为 303 响应,对 Location 执行 GET [...]

所有流行的浏览器都是如此,你对此无能为力。

答案2

TL;DR 如果您想要完全重定向到新资源并且方法和请求主体不应改变,请使用 308 而不是 301 或 302。

301 是永久重定向,但 302 是临时的,因此使用 302 时,搜索引擎不会更改与该网站关联的 URL。301 和 302 表示方法和主体不应更改,但并非所有用户代理都符合这一点。请阅读 Mozilla 的以下解释:

超文本传输​​协议 (HTTP) 302 找到重定向状态响应代码表示请求的资源已临时移至 Location 标头提供的 URL。浏览​​器重定向到此页面,但搜索引擎不会更新其指向资源的链接(用“SEO 术语”来说,即“链接汁”未发送到新 URL)。即使规范要求在执行重定向时不更改方法(和主体),但并非所有用户代理都符合此要求 - 您仍然可以在那里找到这种类型的错误软件。因此,建议仅将 302 代码设置为 GET 或 HEAD 方法的响应,并使用 307 临时重定向,因为在这种情况下明确禁止更改方法。如果您希望将使用的方法更改为 GET,请改用 303 查看其他。当您想对不是上传资源的 PUT 方法做出响应,而是给出确认消息(例如:“您已成功上传 XYZ”)时,这很有用。

308 和 307 都会重定向到新资源,但它们保证请求的主体和方法不会改变。不同之处在于 308 是永久的,而 307 是临时的,因此 308 会向搜索引擎发出信号以更改 URL。请看:

307 和 302 之间的唯一区别是 307 保证在发出重定向请求时方法和主体不会改变。对于 302,一些旧客户端错误地将方法更改为 GET:非 GET 方法和 302 的行为在 Web 上是不可预测的,而 307 的行为是可预测的。对于 GET 请求,它们的行为是相同的。

答案3

我发现POST /api/brand被转换为,GET /api/brand因为我正在使用的 Web 应用程序 ( flask-restful) 发出了“无效”请求。如果我使用POST /api/brand/(注意尾随的/),它就会成功。

答案4

我遇到的问题是,我发出了一个 HTTP 请求,但该请求被 301 重定向到 HTTPS。使用 HTTPS 发送请求可以解决这个问题。

相关内容