nginx redirects POST requests to GET request

11,798

Solution 1

I've found solution. When I did POST request, I used url which ends with slash, like http://myapp.com/tasks/easy_task/calculate/

When I used url without slash in the end, like http://myapp.com/tasks/easy_task/calculate everything works perfectly!

I think it is because of this rule

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

I am closing this issue. Tomorrow.

Solution 2

TL;DR If you want to completely redirect to a new resource and method and body of the requests should not be changed use 308 instead of 301 or 302.

301 is permanent redirect but 302 is temporary so search engines don't change urls associated with that website when 302 is used.
301 and 302 indicated method and body should not be altered, but not all user agents align with that. Read this explanation from Mozilla:

The HyperText Transfer Protocol (HTTP) 302 Found redirect status response code indicates that the resource requested has been temporarily moved to the URL given by the Location header. A browser redirects to this page but search engines don't update their links to the resource (in 'SEO-speak', it is said that the 'link-juice' is not sent to the new URL). Even if the specification requires the method (and the body) not to be altered when the redirection is performed, not all user-agents conform here - you can still find this type of bugged software out there. It is therefore recommended to set the 302 code only as a response for GET or HEAD methods and to use 307 Temporary Redirect instead, as the method change is explicitly prohibited in that case. In the cases where you want the method used to be changed to GET, use 303 See Other instead. This is useful when you want to give a response to a PUT method that is not the uploaded resource but a confirmation message such as: 'you successfully uploaded XYZ'.

308 and 307 both permanently redirect to a new resource but they guarantee body and method of request won't be altered. the difference is that 308 is permanent and 307 is temporary, so 308 will signal search engines to change urls. see this:

The only difference between 307 and 302 is that 307 guarantees that the method and the body will not be changed when the redirected request is made. With 302, some old clients were incorrectly changing the method to GET: the behavior with non-GET methods and 302 is then unpredictable on the Web, whereas the behavior with 307 is predictable. For GET requests, their behavior is identical.

Solution 3

In my case, the redirect meant if I POST to http://... the proxy_pass is converted to a GET.

Change the URL to an https://... and it is passed on as a POST as intended.

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;
}
Share:
11,798
Zhomart
Author by

Zhomart

Ruby developer

Updated on June 12, 2022

Comments

  • Zhomart
    Zhomart almost 2 years

    I have Rails 4.1 application with runs on puma web server. I use nginx as a proxy server. Several days ago everything worked very well. I updated my application, and suddenly some POST requests started to redirected to same url but as GET request. I've tried rollback to previous working versions, no success.

    I found very interesting behaviour. I tested my API with curl.

    • If I did POST request to the url http://myapp.com/tasks/easy_task/calculate/ it redirects to same url but as GET request.
    • Then I did POSTrequest to http://myapp.com/, returned 404
    • Then I did POSTrequest to http://myapp.com/tasks, returned 404
    • Then I did POSTrequest to http://myapp.com/tasks/easy_task, returned 404
    • Then I did POSTrequest to http://myapp.com/tasks/easy_task/calculate, returned 200. YAY!

    Same thing happened when I used chrome's app Postman. First it redirected, but after previous steps it works well.

    I use this app in my other application. I use RestClient to make http requests. When I try to make POST request it raises an exception RestClient::MovedPermanently (301 Moved Permanently).

    • I reinstalled nginx to 1.7.3.
    • Restarted server (virtual machine)
    • re deployed my app, deployed previous versions
    • no success :(

    I found similar questions on stackoverflow, but non of them gave me clue to fix this issue. I hope you can help me to solve this problem. Thanks in advance!

    Similar questions: - POST request turns into GET request - POST request mysteriously turn into GET request

    nginx config:

    $ 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;
    }
    

    Puma

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

    Example of 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"
    
  • Chris.Zou
    Chris.Zou over 9 years
    My condition was just the opposite, I was posting to /tasks and my location definition is /tasks/. Thanks to your topic here, I found the solution! :)
  • Ollie Beumkes
    Ollie Beumkes over 2 years
    Surely this would assume the person changing http to https would have SSL enabled? Interesting point though, may be worth enabling SSL and seeing if the issue goes away!