Nginx将POST请求重定向为GET请求

13

我有一个运行在puma Web服务器上的Rails 4.1应用程序。我使用nginx作为代理服务器。几天前一切正常。我更新了我的应用程序,突然一些POST请求开始以相同的URL重定向为GET请求。我尝试回滚到之前工作的版本,但没有成功。

我发现了非常有趣的行为。我用curl测试了我的API。

  • 如果我对URL http://myapp.com/tasks/easy_task/calculate/进行POST请求,则会将其重定向为相同的URL,但是以GET请求。
  • 然后我对http://myapp.com/进行POST请求,返回404
  • 然后我对http://myapp.com/tasks进行POST请求,返回404
  • 然后我对http://myapp.com/tasks/easy_task进行POST请求,返回404
  • 然后我对http://myapp.com/tasks/easy_task/calculate进行POST请求,返回200。太好了!

当我使用Chrome的应用程序Postman时也发生了同样的情况。首先是重定向,但是在完成前几个步骤后它可以正常工作。

我在我的另一个应用程序中使用这个应用。我使用RestClient发出HTTP请求。当我尝试发起POST请求时,它会引发异常RestClient::MovedPermanently (301 Moved Permanently)

  • 我将nginx重新安装为1.7.3
  • 重启服务器(虚拟机)
  • 重新部署我的应用程序,部署以前的版本
  • 没有成功 :(

我在stackoverflow上发现了类似的问题,但是没有一个能够给我解决这个问题的线索。我希望您能帮助我解决这个问题。提前致谢!

类似的问题: - POST request turns into GET request - POST request mysteriously turn into GET request

nginx配置:

$ cat /etc/nginx/sites-enabled/myapp.com.conf
# The file generated by Chef for mycompany

upstream myapp_mycompany_com {
  server unix:/tmp/myapp.com-puma.sock;
}

server {
  server_name  myapp.com;
  listen       80;

  access_log /var/log/nginx/myapp.com-access.log;
  error_log /var/log/nginx/myapp.com-error.log;

  root /home/projects/mycompany/myapp.com/current/public;

  gzip on;
  gzip_types text/plain text/xml application/xml application/xml+rss
             text/css text/javascript application/javascript application/json;

  error_page 551 =503 @maintenance;
  location @maintenance {
    rewrite ^(.*)$ /system/maintenance.html break;
  }
  set $maintenance 0;
  if (-f $document_root/system/maintenance.html) {
    set $maintenance 1;
  }

  if ($request_uri = /favicon.ico) {
    # Browsers will try to get favicon if it's not returned with 200ok status
    set $maintenance 0;
  }
  if ($maintenance) {
    # There can be several reasons for 503 error. We custom return 551 error
    # to ensure maintenance.html is only shown when it's really maintenance
    return 551;
  }

  rewrite ^/(.*)/$ /$1 permanent; # Truncate trailing slashes
  try_files $uri @rails;

  expires -1;

  location = /favicon.ico {
    try_files $uri =204;
    access_log off;
    log_not_found off;
  }

  location @rails {
    proxy_pass http://myapp_mycompany_com;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_intercept_errors on;
    expires -1;
  }

  error_page 500 502 503 504 /500.html;
  error_page 403 /403.html;
  error_page 404 /404.html;

  client_max_body_size 50M;
  keepalive_timeout 10;
}

彪马

$ bundle exec puma -d -e production -b unix:///tmp/myapp.com-puma.sock --pidfile /home/projects/mycompany/myapp.com/shared/tmp/pids/puma.pid
$

access.log的示例

123.123.123.123 - - [11/Jul/2014:05:44:17 +0000] "POST /tasks/easy_task/calculate/ HTTP/1.1" 301 184 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2073.0 Safari/537.36"
123.123.123.123 - - [11/Jul/2014:05:44:17 +0000] "GET /tasks/easy_task/calculate HTTP/1.1" 404 713 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2073.0 Safari/537.36"

...

123.123.123.123 - - [11/Jul/2014:06:04:17 +0000] "POST / HTTP/1.1" 404 713 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2073.0 Safari/537.36"
123.123.123.123 - - [11/Jul/2014:06:04:26 +0000] "POST /tasks HTTP/1.1" 404 713 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2073.0 Safari/537.36"
123.123.123.123 - - [11/Jul/2014:06:04:36 +0000] "POST /tasks/easy_task HTTP/1.1" 404 713 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2073.0 Safari/537.36"
123.123.123.123 - - [11/Jul/2014:06:04:42 +0000] "POST /tasks/easy_task/calculate HTTP/1.1" 200 104 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2073.0 Safari/537.36"

可能是由你的Rails应用程序生成的所有这些答案都是404错误。你可以展示一下Nginx的错误日志吗? - mr_tron
nginx的error.log是空的。所有404错误都是rails应用程序的错误。当出现301时,rails应用程序甚至没有收到请求。我关闭了rails应用程序,但仍然得到301。 - Zhomart
3个回答

12

TL;DR 如果你想完全重定向到一个新的资源,并且请求的方法和内容不应该改变,使用 308 而不是 301 或 302。

301 是永久重定向,但 302 是临时重定向,因此当使用 302 时,搜索引擎不会更改与该网站相关联的网址。
301 和 302 表示方法和内容不应被更改,但并非所有用户代理都遵守这一点。请阅读 Mozilla 的解释:

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

308和307都是永久重定向到一个新的资源,但它们保证请求的方法和内容不会被更改。它们之间的区别在于,308是永久性的,而307是临时性的,因此308会通知搜索引擎更改URL。请参见以下内容:

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


10

我已找到解决方案。当我进行POST请求时,我使用以斜杠结尾的URL,例如:http://myapp.com/tasks/easy_task/calculate/

当我使用没有斜杠结尾的URL,例如:http://myapp.com/tasks/easy_task/calculate时,一切都完美运行!

我认为这是由于这个规则。

rewrite ^/(.*)/$ /$1 permanent; # Truncate trailing slashes

我会在明天关闭这个问题。


2
我的情况恰好相反,我正在发布到/tasks,而我的位置定义是/tasks/。感谢您在这里提供的主题,我找到了解决方案! :) - Chris.Zou

2
在我的情况下,重定向意味着如果我POST到http://...,则proxy_pass会转换为GET。
将URL更改为https://...,则按预期作为POST传递。
location /dc1 {
    proxy_pass http://localhost:8000;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host www.example.com;
}

这是否意味着更改HTTP为HTTPS的人已启用SSL?不过这是一个有趣的观点,值得启用SSL并查看问题是否消失! - Ollie Beumkes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接