Understanding Origin Failover with Nginx proxy_next_upstream
When operating Nginx as a reverse proxy, handling upstream server failures gracefully is essential for service reliability. The ngx_http_proxy_module provides the proxy_next_upstream directive to define the specific conditions under which an incoming client request should be passed to the next backend server when the current server fails.
By default, Nginx sets proxy_next_upstream error timeout in http, server, and location contexts. Under this default policy, Nginx retries another server only if an error occurs while establishing a connection with the server, passing a request to it, or reading the response header, or if a corresponding timeout occurs.
Conditions That Trigger Failover
The proxy_next_upstream directive accepts specific failure conditions, as well as an explicit off parameter to disable passing a request to the next server entirely:
- error: An error occurred while establishing a connection with the server, passing a request to it, or reading the response header. (Always considered an unsuccessful attempt).
- timeout: A timeout occurred while establishing a connection with the server, passing a request to it, or reading the response header. (Always considered an unsuccessful attempt).
- invalid_header: A server returned an empty or invalid response. (Always considered an unsuccessful attempt).
- denied: The server denied the connection (annotated as appearing in version 1.29.3 as part of commercial subscription features; always considered an unsuccessful attempt).
- HTTP Status Codes:
http_500,http_502,http_503,http_504, andhttp_429(withhttp_429annotated as appearing in version 1.11.13). These status codes are considered unsuccessful attempts only if explicitly specified in the directive. - Excluded Status Codes:
http_403andhttp_404are never considered unsuccessful attempts.
How an attempt is classified also directly affects upstream server availability tracking. Under the ngx_http_upstream_module, each upstream server entry can define max_fails (defaulting to 1) and fail_timeout (defaulting to 10 seconds). What counts as an unsuccessful attempt is defined by directives including proxy_next_upstream. An attempt counted as unsuccessful contributes toward the server’s max_fails threshold within the fail_timeout duration to consider the server unavailable for that duration. If there is only a single server in an upstream group, max_fails and fail_timeout are ignored, and that server is never considered unavailable.
Limiting Retries: Time Boundaries and Tries
The documentation states that passing a request to the next server can be limited by the number of tries and by time.
For time limits, Nginx provides the proxy_next_upstream_timeout directive:
proxy_next_upstream_timeout time;(Default:proxy_next_upstream_timeout 0;): Configured inhttp,server, andlocationcontexts. Documented as appearing in version 1.7.5.
The Response Transfer Limitation and Buffering
A crucial operational boundary governs passing requests to the next server: passing a request to the next server is only possible if nothing has been sent to a client yet. If an error or timeout occurs in the middle of transferring a response, fixing this is impossible.
In addition, response buffering settings affect how response data is delivered:
- Under
proxy_buffering on;(the default), Nginx receives a response from the proxied server as soon as possible, saving it into the buffers configured byproxy_buffer_sizeandproxy_buffers. If the whole response does not fit into memory, a part of it can be saved to a temporary file viaproxy_max_temp_file_sizeandproxy_temp_file_write_size. - When
proxy_buffering off;is set, the response is passed to a client synchronously, immediately as it is received, and Nginx will not try to read the whole response from the proxied server.
Handling Non-Idempotent Requests
Normally, requests with a non-idempotent method (POST, LOCK, PATCH) are not passed to the next server if a request has already been sent to an upstream server (annotated as appearing in version 1.9.13). Enabling the non_idempotent option in proxy_next_upstream explicitly allows retrying such requests.
Configuration Example
The following example demonstrates specifying failure triggers and setting a retry timeout window in an Nginx configuration:
upstream backend_cluster {
server 10.0.0.1:8080 max_fails=2 fail_timeout=10s;
server 10.0.0.2:8080 max_fails=2 fail_timeout=10s;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_cluster;
proxy_buffering on;
# Failover triggers
proxy_next_upstream error timeout http_502 http_503;
# Limit retry time window
proxy_next_upstream_timeout 5s;
}
}

Text version of the diagrams
- When NGINX Can Retry: Before response — Another upstream may be tried; Client receives data — Retry is no longer possible; Non-idempotent — Retry requires explicit option
- Three Failover Controls: Conditions — Choose errors or statuses; Timeout window — Limit total retry time; Server tracking — max_fails and fail_timeout
Research Method and Limitations
This technical explainer was prepared exclusively from the supplied public documentation excerpts for ngx_http_proxy_module and ngx_http_upstream_module. Competing community coverage from DigitalOcean was unavailable because the retrieved excerpt contained only site navigation. A precise global Nginx version scope cannot be asserted across all described behaviors; the supplied excerpts provide only point-in-time reference text with specific version annotations noted for individual features (such as proxy_next_upstream_timeout in 1.7.5, non_idempotent in 1.9.13, http_429 in 1.11.13, and denied in 1.29.3 under commercial subscription). Furthermore, while the documentation notes that passing requests to the next server can be limited by the number of tries, the specific directive syntax and defaults for tries limits were omitted in the supplied excerpts and are excluded here.



