]> git.kaiwu.me - nginx.git/commitdiff
Upstream: special handling of the "Host" header in proxy and gRPC
authorRoman Arutyunyan <arut@nginx.com>
Tue, 21 Jul 2026 14:14:39 +0000 (18:14 +0400)
committerRoman Arutyunyan <arutyunyan.roman@gmail.com>
Fri, 7 Aug 2026 14:54:23 +0000 (18:54 +0400)
HTTP/2 and gRPC requests are now always sent with the ":authority"
pseudo-header, while the "Host" header is no longer passed.
Where a host value is set explicitly with "proxy_set_header Host"
or "grpc_set_header Host", that value is conveyed in ":authority".
As per RFC 9113, Section 8.3.1, clients and intermediaries MUST
generate an ":authority" pseudo-header to convey authority information.

For HTTP/1.1, HTTP/2 and gRPC, if the explicit value is empty, then
the value of $proxy_host is used instead.  As per RFC 9110, Sections
4.2.1 and 4.2.2, the "http" and "https" URI schemes MUST contain a
non-empty host identifier.  The change enforces this rule by
introducing a default.

Closes: https://github.com/nginx/nginx/issues/1426
src/http/modules/ngx_http_grpc_module.c
src/http/modules/ngx_http_proxy_module.c
src/http/modules/ngx_http_proxy_module.h
src/http/modules/ngx_http_proxy_v2_module.c

index 3d0e9d7ee2ced047d5f5045040f95be49d9f93a6..6f1752e5476144f90eaab21dd7ce331438a13d0c 100644 (file)
@@ -25,7 +25,7 @@ typedef struct {
     ngx_array_t               *headers_source;
 
     ngx_str_t                  host;
-    ngx_uint_t                 host_set;
+    ngx_http_complex_value_t  *host_value;
 
     ngx_array_t               *grpc_lengths;
     ngx_array_t               *grpc_values;
@@ -739,6 +739,7 @@ ngx_http_grpc_create_request(ngx_http_request_t *r)
                                   key_len, val_len, uri_len;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
+    ngx_str_t                     host;
     ngx_uint_t                    i, next;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
@@ -809,18 +810,28 @@ ngx_http_grpc_create_request(ngx_http_request_t *r)
 
     /* :authority header */
 
-    if (!glcf->host_set) {
-        if (ctx->host.len > NGX_HTTP_V2_MAX_FIELD) {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "too long http2 host: \"%V\"", &ctx->host);
-            return NGX_ERROR;
-        }
+    host.len = 0;
 
-        len += 1 + NGX_HTTP_V2_INT_OCTETS + ctx->host.len;
+    if (glcf->host_value
+        && ngx_http_complex_value(r, glcf->host_value, &host) != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
 
-        if (tmp_len < ctx->host.len) {
-            tmp_len = ctx->host.len;
-        }
+    if (host.len == 0) {
+        host = ctx->host;
+    }
+
+    if (host.len > NGX_HTTP_V2_MAX_FIELD) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "too long http2 host: \"%V\"", &host);
+        return NGX_ERROR;
+    }
+
+    len += 1 + NGX_HTTP_V2_INT_OCTETS + host.len;
+
+    if (tmp_len < host.len) {
+        tmp_len = host.len;
     }
 
     /* other headers */
@@ -1051,14 +1062,11 @@ ngx_http_grpc_create_request(ngx_http_request_t *r)
                        "grpc header: \":path: %V\"", &r->uri);
     }
 
-    if (!glcf->host_set) {
-        *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
-        b->last = ngx_http_v2_write_value(b->last, ctx->host.data,
-                                          ctx->host.len, tmp);
+    *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
+    b->last = ngx_http_v2_write_value(b->last, host.data, host.len, tmp);
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "grpc header: \":authority: %V\"", &ctx->host);
-    }
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "grpc header: \":authority: %V\"", &host);
 
     ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
 
@@ -4517,7 +4525,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
      *     conf->headers.values = NULL;
      *     conf->headers.hash = { NULL, 0 };
      *     conf->host = { 0, NULL };
-     *     conf->host_set = 0;
+     *     conf->host_value = NULL;
      *     conf->ssl = 0;
      *     conf->ssl_protocols = 0;
      *     conf->ssl_ciphers = { 0, NULL };
@@ -4725,7 +4733,7 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
 
     if (conf->headers_source == prev->headers_source) {
         conf->headers = prev->headers;
-        conf->host_set = prev->host_set;
+        conf->host_value = prev->host_value;
     }
 
     rc = ngx_http_grpc_init_headers(cf, conf, &conf->headers,
@@ -4743,7 +4751,7 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
         && conf->headers_source == prev->headers_source)
     {
         prev->headers = conf->headers;
-        prev->host_set = conf->host_set;
+        prev->host_value = conf->host_value;
     }
 
     return NGX_CONF_OK;
@@ -4754,16 +4762,17 @@ static ngx_int_t
 ngx_http_grpc_init_headers(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf,
     ngx_http_grpc_headers_t *headers, ngx_keyval_t *default_headers)
 {
-    u_char                       *p;
-    size_t                        size;
-    uintptr_t                    *code;
-    ngx_uint_t                    i;
-    ngx_array_t                   headers_names, headers_merged;
-    ngx_keyval_t                 *src, *s, *h;
-    ngx_hash_key_t               *hk;
-    ngx_hash_init_t               hash;
-    ngx_http_script_compile_t     sc;
-    ngx_http_script_copy_code_t  *copy;
+    u_char                            *p;
+    size_t                             size;
+    uintptr_t                         *code;
+    ngx_uint_t                         i;
+    ngx_array_t                        headers_names, headers_merged;
+    ngx_keyval_t                      *src, *s, *h;
+    ngx_hash_key_t                    *hk;
+    ngx_hash_init_t                    hash;
+    ngx_http_script_compile_t          sc;
+    ngx_http_script_copy_code_t       *copy;
+    ngx_http_compile_complex_value_t   ccv;
 
     if (headers->hash.buckets) {
         return NGX_OK;
@@ -4799,7 +4808,23 @@ ngx_http_grpc_init_headers(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf,
             if (src[i].key.len == 4
                 && ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0)
             {
-                conf->host_set = 1;
+                conf->host_value = ngx_pcalloc(cf->pool,
+                                             sizeof(ngx_http_complex_value_t));
+                if (conf->host_value == NULL) {
+                    return NGX_ERROR;
+                }
+
+                ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+                ccv.cf = cf;
+                ccv.value = &src[i].value;
+                ccv.complex_value = conf->host_value;
+
+                if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                continue;
             }
 
             s = ngx_array_push(&headers_merged);
index 3ae8c97664584a73f2efb5f0550e41db97e37a19..384c2ee70a9d643814c223a7d43654e24caa8787 100644 (file)
@@ -759,7 +759,7 @@ static char  ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
 
 
 static ngx_keyval_t  ngx_http_proxy_headers[] = {
-    { ngx_string("Host"), ngx_string("$proxy_internal_host") },
+    { ngx_string("Host"), ngx_string("") },
     { ngx_string("Connection"), ngx_string("") },
     { ngx_string("Proxy-Connection"), ngx_string("") },
     { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
@@ -788,7 +788,7 @@ static ngx_str_t  ngx_http_proxy_hide_headers[] = {
 #if (NGX_HTTP_CACHE)
 
 static ngx_keyval_t  ngx_http_proxy_cache_headers[] = {
-    { ngx_string("Host"), ngx_string("$proxy_internal_host") },
+    { ngx_string("Host"), ngx_string("") },
     { ngx_string("Connection"), ngx_string("") },
     { ngx_string("Proxy-Connection"), ngx_string("") },
     { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
@@ -825,10 +825,6 @@ static ngx_http_variable_t  ngx_http_proxy_vars[] = {
     { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },
 #endif
 
-    { ngx_string("proxy_internal_host"), NULL,
-      ngx_http_proxy_host_variable, 1,
-      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
-
     { ngx_string("proxy_internal_body_length"), NULL,
       ngx_http_proxy_internal_body_length_variable, 0,
       NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
@@ -904,8 +900,6 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    ctx->legacy = 1;
-
     ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
 
     u = r->upstream;
@@ -1193,7 +1187,7 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
                                   key_len, val_len;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
-    ngx_str_t                     method;
+    ngx_str_t                     method, host;
     ngx_uint_t                    i, unparsed_uri;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
@@ -1231,6 +1225,20 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
 
+    host.len = 0;
+
+    if (plcf->host_value
+        && ngx_http_complex_value(r, plcf->host_value, &host) != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    if (plcf->host_value == NULL
+        || (host.len == 0 && plcf->http_version == NGX_HTTP_VERSION_11))
+    {
+        host = ctx->vars.host_header;
+    }
+
     if (method.len == 4
         && ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0)
     {
@@ -1300,6 +1308,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
         ctx->internal_body_length = r->headers_in.content_length_n;
     }
 
+    if (host.len) {
+        len += sizeof("Host: ") - 1 + host.len + sizeof(CRLF) - 1;
+    }
+
     le.ip = headers->lengths->elts;
     le.request = r;
     le.flushed = 1;
@@ -1410,6 +1422,12 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
                              sizeof(ngx_http_proxy_version) - 1);
     }
 
+    if (host.len) {
+        b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
+        b->last = ngx_cpymem(b->last, host.data, host.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+
     ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
 
     e.ip = headers->values->elts;
@@ -2727,11 +2745,6 @@ ngx_http_proxy_host_variable(ngx_http_request_t *r,
         return NGX_OK;
     }
 
-    if (data == 1 && !ctx->legacy) {
-        v->not_found = 1;
-        return NGX_OK;
-    }
-
     v->len = ctx->vars.host_header.len;
     v->valid = 1;
     v->no_cacheable = 0;
@@ -3542,7 +3555,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
      *     conf->headers.values = NULL;
      *     conf->headers.hash = { NULL, 0 };
      *     conf->headers_cache.lengths = NULL;
-     *     conf->host_set = 0;
+     *     conf->host_value = NULL;
      *     conf->headers_cache.values = NULL;
      *     conf->headers_cache.hash = { NULL, 0 };
      *     conf->body_lengths = NULL;
@@ -4126,7 +4139,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
 #if (NGX_HTTP_CACHE)
         conf->headers_cache = prev->headers_cache;
 #endif
-        conf->host_set = prev->host_set;
+        conf->host_value = prev->host_value;
     }
 
     rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,
@@ -4159,7 +4172,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
 #if (NGX_HTTP_CACHE)
         prev->headers_cache = conf->headers_cache;
 #endif
-        prev->host_set = conf->host_set;
+        prev->host_value = conf->host_value;
     }
 
     return NGX_CONF_OK;
@@ -4170,16 +4183,17 @@ static ngx_int_t
 ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
     ngx_http_proxy_headers_t *headers, ngx_keyval_t *default_headers)
 {
-    u_char                       *p;
-    size_t                        size;
-    uintptr_t                    *code;
-    ngx_uint_t                    i;
-    ngx_array_t                   headers_names, headers_merged;
-    ngx_keyval_t                 *src, *s, *h;
-    ngx_hash_key_t               *hk;
-    ngx_hash_init_t               hash;
-    ngx_http_script_compile_t     sc;
-    ngx_http_script_copy_code_t  *copy;
+    u_char                            *p;
+    size_t                             size;
+    uintptr_t                         *code;
+    ngx_uint_t                         i;
+    ngx_array_t                        headers_names, headers_merged;
+    ngx_keyval_t                      *src, *s, *h;
+    ngx_hash_key_t                    *hk;
+    ngx_hash_init_t                    hash;
+    ngx_http_script_compile_t          sc;
+    ngx_http_script_copy_code_t       *copy;
+    ngx_http_compile_complex_value_t   ccv;
 
     if (headers->hash.buckets) {
         return NGX_OK;
@@ -4215,7 +4229,23 @@ ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
             if (src[i].key.len == 4
                 && ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0)
             {
-                conf->host_set = 1;
+                conf->host_value = ngx_pcalloc(cf->pool,
+                                             sizeof(ngx_http_complex_value_t));
+                if (conf->host_value == NULL) {
+                    return NGX_ERROR;
+                }
+
+                ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+                ccv.cf = cf;
+                ccv.value = &src[i].value;
+                ccv.complex_value = conf->host_value;
+
+                if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                continue;
             }
 
             s = ngx_array_push(&headers_merged);
index a0f79bc13c116bae09557114b5cd39b68ab29323..0675122b247873da21832043a849010fd097bba3 100644 (file)
@@ -49,7 +49,7 @@ typedef struct {
     ngx_http_proxy_headers_t       headers_cache;
 #endif
     ngx_array_t                   *headers_source;
-    ngx_uint_t                     host_set;
+    ngx_http_complex_value_t      *host_value;
 
     ngx_array_t                   *proxy_lengths;
     ngx_array_t                   *proxy_values;
@@ -102,7 +102,6 @@ typedef struct {
     unsigned                       head:1;
     unsigned                       internal_chunked:1;
     unsigned                       header_sent:1;
-    unsigned                       legacy:1;
 } ngx_http_proxy_ctx_t;
 
 
index 372ab05e9e480e63d312387b62eafbad636c63b6..cc3f090ca2b10832e5ed53fe916c8f71a7100f72 100644 (file)
@@ -326,7 +326,7 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
                                   loc_len, body_len;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
-    ngx_str_t                     method, *host;
+    ngx_str_t                     method, host;
     ngx_uint_t                    i, next, unparsed_uri;
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
@@ -445,20 +445,28 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
 
     /* :authority header */
 
-    host = &ctx->ctx.vars.host_header;
+    host.len = 0;
 
-    if (!plcf->host_set) {
-        if (host->len > NGX_HTTP_V2_MAX_FIELD) {
-            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                          "too long http2 host: \"%V\"", host);
-            return NGX_ERROR;
-        }
+    if (plcf->host_value
+        && ngx_http_complex_value(r, plcf->host_value, &host) != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
 
-        len += 1 + NGX_HTTP_V2_INT_OCTETS + host->len;
+    if (host.len == 0) {
+        host = ctx->ctx.vars.host_header;
+    }
 
-        if (tmp_len < host->len) {
-            tmp_len = host->len;
-        }
+    if (host.len > NGX_HTTP_V2_MAX_FIELD) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "too long http2 host: \"%V\"", &host);
+        return NGX_ERROR;
+    }
+
+    len += 1 + NGX_HTTP_V2_INT_OCTETS + host.len;
+
+    if (tmp_len < host.len) {
+        tmp_len = host.len;
     }
 
     /* other headers */
@@ -719,13 +727,11 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
                        val_tmp);
     }
 
-    if (!plcf->host_set) {
-        *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
-        b->last = ngx_http_v2_write_value(b->last, host->data, host->len, tmp);
+    *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
+    b->last = ngx_http_v2_write_value(b->last, host.data, host.len, tmp);
 
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "http proxy header: \":authority: %V\"", host);
-    }
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http proxy header: \":authority: %V\"", &host);
 
     ngx_memzero(&e, sizeof(ngx_http_script_engine_t));