[nginx] Upstream: unexpected 1xx interim responses now ignored.

Maxim Dounin mdounin at mdounin.ru
Mon Aug 18 00:30:24 UTC 2025


details:   http://freenginx.org/hg/nginx/rev/96ce0adabccb
branches:  
changeset: 9409:96ce0adabccb
user:      Maxim Dounin <mdounin at mdounin.ru>
date:      Mon Aug 18 03:19:28 2025 +0300
description:
Upstream: unexpected 1xx interim responses now ignored.

Unexpected 1xx interim informational responses received before the final
response are now parsed and ignored.  This is required for HTTP/1.1
and above, and expected to provide better interoperability.

Notable exception is 101 (Switching Protocols), which is rejected unless
requested by the client, as it is expected to be followed by other
protocol data which won't be able to parse anyway.  Similarly, invalid
responses with status codes below 100 are rejected as well.

Amount of 1xx responses which can be received from an upstream server
and ignored is generally limited by the buffer size.  For gRPC, since
buffer can be reset between reading HEADERS frames, total number of
1xx responses is additionally limited.  The limit is set to 10, which
happens to match the limit used by Apache.

diffstat:

 src/http/modules/ngx_http_fastcgi_module.c |  21 ++++++++++--
 src/http/modules/ngx_http_grpc_module.c    |  47 +++++++++++++++++++++++++----
 src/http/modules/ngx_http_proxy_module.c   |  23 ++++++++++++--
 src/http/modules/ngx_http_scgi_module.c    |  22 +++++++++++--
 src/http/modules/ngx_http_uwsgi_module.c   |  22 +++++++++++--
 src/http/ngx_http_upstream.c               |   4 +-
 src/http/ngx_http_upstream.h               |   2 +
 7 files changed, 115 insertions(+), 26 deletions(-)

diffs (265 lines):

diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2063,10 +2063,9 @@ ngx_http_fastcgi_process_header(ngx_http
                     ngx_str_set(&u->headers_in.status_line, "200 OK");
                 }
 
-                if (u->headers_in.status_n < NGX_HTTP_OK) {
-
-                    /* reject 1xx responses */
-
+                if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+                    || u->headers_in.status_n < NGX_HTTP_CONTINUE)
+                {
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                                   "upstream sent unexpected status \"%V\"",
                                   u->headers_in.status_line.len
@@ -2074,6 +2073,20 @@ ngx_http_fastcgi_process_header(ngx_http
                                   : &u->headers_in.status->value);
 
                     return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+
+                } else if (u->headers_in.status_n < NGX_HTTP_OK) {
+
+                    /* ignore unexpected 1xx responses */
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "http fastcgi 1xx ignored");
+
+                    if (ngx_http_upstream_clear_headers(r, u) != NGX_OK) {
+                        return NGX_ERROR;
+                    }
+
+                    rc = NGX_OK;
+                    break;
                 }
 
                 break;
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -82,6 +82,7 @@ typedef struct {
 
     ngx_uint_t                 pings;
     ngx_uint_t                 settings;
+    ngx_uint_t                 headers;
 
     off_t                      length;
 
@@ -1218,6 +1219,7 @@ ngx_http_grpc_reinit_request(ngx_http_re
     ctx->connection = NULL;
     ctx->pings = 0;
     ctx->settings = 0;
+    ctx->headers = 0;
 
     return NGX_OK;
 }
@@ -1860,13 +1862,6 @@ ngx_http_grpc_process_header(ngx_http_re
                         return NGX_HTTP_UPSTREAM_INVALID_HEADER;
                     }
 
-                    if (status < NGX_HTTP_OK) {
-                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                                      "upstream sent unexpected :status \"%V\"",
-                                      status_line);
-                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
-                    }
-
                     u->headers_in.status_n = status;
 
                     ctx->status = 1;
@@ -1910,6 +1905,44 @@ ngx_http_grpc_process_header(ngx_http_re
                 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                                "grpc header done");
 
+                if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+                    || u->headers_in.status_n < NGX_HTTP_CONTINUE)
+                {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "upstream sent unexpected status \"%03ui\"",
+                                  u->headers_in.status_n);
+
+                    return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+
+                } else if (u->headers_in.status_n < NGX_HTTP_OK) {
+
+                    /* ignore unexpected 1xx responses */
+
+                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "grpc 1xx ignored");
+
+                    if (ctx->end_stream) {
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "upstream sent 1xx response "
+                                      "with end stream flag");
+                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+                    }
+
+                    if (ctx->headers++ > 10) {
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "upstream sent too many 1xx responses");
+                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+                    }
+
+                    if (ngx_http_upstream_clear_headers(r, u) != NGX_OK) {
+                        return NGX_ERROR;
+                    }
+
+                    ctx->status = 0;
+
+                    break;
+                }
+
                 if (ctx->end_stream) {
                     u->headers_in.content_length_n = 0;
 
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -2026,15 +2026,30 @@ ngx_http_proxy_process_header(ngx_http_r
                 u->keepalive = 0;
                 u->upgrade = 1;
 
-            } else if (u->headers_in.status_n < NGX_HTTP_OK) {
-
-                /* reject unexpected 1xx responses */
-
+            } else if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+                       || u->headers_in.status_n < NGX_HTTP_CONTINUE)
+            {
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "upstream sent unexpected status \"%V\"",
                               &u->headers_in.status_line);
 
                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+
+            } else if (u->headers_in.status_n < NGX_HTTP_OK) {
+
+                /* ignore unexpected 1xx responses */
+
+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "http proxy 1xx ignored");
+
+                u->keepalive = 0;
+                u->process_header = ngx_http_proxy_process_status_line;
+
+                if (ngx_http_upstream_clear_headers(r, u) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                return ngx_http_proxy_process_status_line(r);
             }
 
             return NGX_OK;
diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c
--- a/src/http/modules/ngx_http_scgi_module.c
+++ b/src/http/modules/ngx_http_scgi_module.c
@@ -1158,10 +1158,9 @@ ngx_http_scgi_process_header(ngx_http_re
             {
                 u->upgrade = 1;
 
-            } else if (u->headers_in.status_n < NGX_HTTP_OK) {
-
-                /* reject unexpected 1xx responses */
-
+            } else if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+                       || u->headers_in.status_n < NGX_HTTP_CONTINUE)
+            {
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "upstream sent unexpected status \"%V\"",
                               u->headers_in.status_line.len
@@ -1169,6 +1168,21 @@ ngx_http_scgi_process_header(ngx_http_re
                               : &u->headers_in.status->value);
 
                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+
+            } else if (u->headers_in.status_n < NGX_HTTP_OK) {
+
+                /* ignore unexpected 1xx responses */
+
+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "http scgi 1xx ignored");
+
+                u->process_header = ngx_http_scgi_process_status_line;
+
+                if (ngx_http_upstream_clear_headers(r, u) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                return ngx_http_scgi_process_status_line(r);
             }
 
             return NGX_OK;
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -1387,10 +1387,9 @@ ngx_http_uwsgi_process_header(ngx_http_r
             {
                 u->upgrade = 1;
 
-            } else if (u->headers_in.status_n < NGX_HTTP_OK) {
-
-                /* reject unexpected 1xx responses */
-
+            } else if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+                       || u->headers_in.status_n < NGX_HTTP_CONTINUE)
+            {
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "upstream sent unexpected status \"%V\"",
                               u->headers_in.status_line.len
@@ -1398,6 +1397,21 @@ ngx_http_uwsgi_process_header(ngx_http_r
                               : &u->headers_in.status->value);
 
                 return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+
+            } else if (u->headers_in.status_n < NGX_HTTP_OK) {
+
+                /* ignore unexpected 1xx responses */
+
+                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "http uwsgi 1xx ignored");
+
+                u->process_header = ngx_http_uwsgi_process_status_line;
+
+                if (ngx_http_upstream_clear_headers(r, u) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                return ngx_http_uwsgi_process_status_line(r);
             }
 
             return NGX_OK;
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -45,8 +45,6 @@ static void ngx_http_upstream_connect(ng
     ngx_http_upstream_t *u);
 static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
-static ngx_int_t ngx_http_upstream_clear_headers(ngx_http_request_t *r,
-    ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_request(ngx_http_request_t *r,
     ngx_http_upstream_t *u, ngx_uint_t do_write);
 static ngx_int_t ngx_http_upstream_send_request_body(ngx_http_request_t *r,
@@ -2070,7 +2068,7 @@ ngx_http_upstream_reinit(ngx_http_reques
 }
 
 
-static ngx_int_t
+ngx_int_t
 ngx_http_upstream_clear_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
 {
     if (u->headers_in.headers.last) {
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -421,6 +421,8 @@ typedef struct {
 
 ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
 void ngx_http_upstream_init(ngx_http_request_t *r);
+ngx_int_t ngx_http_upstream_clear_headers(ngx_http_request_t *r,
+    ngx_http_upstream_t *u);
 ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
 ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes);
 ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,


More information about the nginx-devel mailing list