[PATCH 12 of 12] Upstream: unexpected 1xx interim responses now ignored

Maxim Dounin mdounin at mdounin.ru
Fri Aug 8 20:09:07 UTC 2025


# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1754683274 -10800
#      Fri Aug 08 23:01:14 2025 +0300
# Node ID f7e18803d4411a20e25a0f7f33bfb7e281cc1739
# Parent  9b2abd883ed2b44c0b48db7506f24d0a326bfb7f
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.

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