15

We are using nginx to load balance requests to our application. We have found that nginx switches to a different upstream server when requests time out (good). However it does this for PUT and POST requests which can cause undesirable results (data stored twice). Is it possible to configure nginx to only retry GET requests on timeout? Or is there another way to solve the problem?

Our configuration is as follows:

upstream mash {
    ip_hash;
    server 127.0.0.1:8081;
    server 192.168.0.11:8081;
}

server {
    ...
    location / {
        proxy_pass http://mash/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        
    }
}
David Tinker
  • 589
  • 1
  • 8
  • 18

5 Answers5

14

Starting in nginx 1.9.13, non-idempotent requests (PUT, POST, etc) are not retried by default. If you can upgrade to this version of later, you can obtain the desired behavior by default.

If for some reason you would like to continue retrying PUT, POST, etc (non-idempotent) requests on 1.9.13 or later, use:

proxy_next_upstream error timeout non_idempotent;
jbielick
  • 103
  • 3
  • 2
    This answer is a bit misleading. The OP didn't want to send the PUT and POST requests to the next upstream server. Since version 1.9.13, the non idempotent requests (PUT, POST, LOCK) are no longer sent to the next upstream server by default. Use `proxy_next_upstream error timeout non_idempotent;` only if you want to send PUT, POST, LOCK requests to the next upstream server on error or timeout. – Kayes Aug 08 '20 at 01:24
6

I know i'm pretty late to the game, but for me this is the top result when searching for this problem, so i wanted to share my solution.

This uses the if directive (with one of the few valid use cases) combined with the custom error handler:

upstream backend {
    server backend1;
    server backend2;
}

server {
    server_name proxy;

    location / {
        error_page 598 = @retry;
        error_page 599 = @no_retry;
        if ($request_method = POST) {
            return 599;
        }
        return 598;
    }

    location @retry {
        proxy_pass http://backend;
    }

    location @no_retry {
        proxy_pass http://backend;
        proxy_next_upstream off;
    }
}
ddelbondio
  • 161
  • 1
  • 1
4

Please see here for doc: proxy_next_upstream

Please note this is an untested gist

https://gist.github.com/wojons/6154645

WojonsTech
  • 350
  • 1
  • 10
  • Actually it didn't work: Nginx says "proxy_next_upstream not allowed here". I tried moving the if blocks into location and got the same error. Using "proxy_next_upstream error" in either location on its own works. – David Tinker Aug 06 '13 at 09:33
  • that is very odd since there documentation clearly says it works in the location context – WojonsTech Aug 06 '13 at 20:17
  • it seems to be the if (...) { } around proxy_next_upstream that nginx doesn't like – David Tinker Aug 07 '13 at 09:18
  • Has anyone tested this? 4 upvotes but it doesn't seem to adhere to valid use cases here: https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/ – EoghanM Feb 05 '16 at 12:58
1

use proxy_method directive

refer to: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_method

yuankui
  • 19
  • 2
  • 2
    It's generally recommended to include the useful information from a link in your answer such that it does not explicitly rely on the link to be useful – BE77Y Jun 17 '15 at 15:52
  • 1
    Welcome to Server Fault! Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Mark Henderson Jun 17 '15 at 22:51
-1

I have same problem in my tomcat server. proxy time out when long request occur. i solved my problem by using proxy_read_timeout. when increase timeout then my request never time outed & not occurred any problem. default time out 60s. reference

location / {
    proxy_pass  http://xxxxxxxxxx.com;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Proto https;
            proxy_redirect off;
            proxy_connect_timeout      800;
            proxy_send_timeout         800;
            proxy_read_timeout         240;     
}
hmtmcse
  • 1
  • 1