[nginx] Proxy: added the "proxy_allow_duplicate_chunked" directive.
Maxim Dounin
mdounin at mdounin.ru
Sun Aug 24 17:15:13 UTC 2025
details: http://freenginx.org/hg/nginx/rev/cfe8cf095ff0
branches:
changeset: 9418:cfe8cf095ff0
user: Maxim Dounin <mdounin at mdounin.ru>
date: Sun Aug 24 20:14:28 2025 +0300
description:
Proxy: added the "proxy_allow_duplicate_chunked" directive.
This directive allows to accept duplicate "Transfer-Encoding: chunked"
header lines. These are invalid, and rejected since 8033:2bf7792c262e
(1.23.0), yet it turns out there are quite a few homegrown proxies, notably
Java-based ones, which emit such duplicate headers.
The "proxy_allow_duplicate_chunked" directive makes it possible to enable
compatibility with such proxies by ignoring duplicate "Transfer-Encoding:
chunked" headers instead of rejecting them.
Prodded by Gennady Bekasov,
https://github.com/freenginx/nginx/issues/11
diffstat:
src/http/modules/ngx_http_fastcgi_module.c | 4 ++--
src/http/modules/ngx_http_grpc_module.c | 1 +
src/http/modules/ngx_http_memcached_module.c | 1 +
src/http/modules/ngx_http_proxy_module.c | 14 ++++++++++++--
src/http/modules/ngx_http_scgi_module.c | 4 ++--
src/http/modules/ngx_http_uwsgi_module.c | 4 ++--
src/http/ngx_http_upstream.c | 2 +-
src/http/ngx_http_upstream.h | 1 +
8 files changed, 22 insertions(+), 9 deletions(-)
diffs (139 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
@@ -2948,10 +2948,10 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
conf->upstream.intercept_errors = NGX_CONF_UNSET;
- /* "fastcgi_cyclic_temp_file" is disabled */
+ /* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
-
conf->upstream.change_buffering = 1;
+ conf->upstream.duplicate_chunked = 0;
conf->catch_stderr = NGX_CONF_UNSET_PTR;
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
@@ -4431,6 +4431,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t
conf->upstream.pass_request_headers = 1;
conf->upstream.pass_request_body = 1;
conf->upstream.force_ranges = 0;
+ conf->upstream.duplicate_chunked = 0;
conf->upstream.pass_trailers = 1;
conf->upstream.preserve_output = 1;
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -627,6 +627,7 @@ ngx_http_memcached_create_loc_conf(ngx_c
conf->upstream.pass_request_headers = 0;
conf->upstream.pass_request_body = 0;
conf->upstream.force_ranges = 1;
+ conf->upstream.duplicate_chunked = 0;
conf->index = NGX_CONF_UNSET;
conf->gzip_flag = NGX_CONF_UNSET_UINT;
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
@@ -695,6 +695,13 @@ static ngx_command_t ngx_http_proxy_com
offsetof(ngx_http_proxy_loc_conf_t, http09),
NULL },
+ { ngx_string("proxy_allow_duplicate_chunked"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.duplicate_chunked),
+ NULL },
+
#if (NGX_HTTP_SSL)
{ ngx_string("proxy_ssl_session_reuse"),
@@ -3392,6 +3399,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
conf->upstream.request_buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
conf->upstream.force_ranges = NGX_CONF_UNSET;
+ conf->upstream.duplicate_chunked = NGX_CONF_UNSET;
conf->upstream.local = NGX_CONF_UNSET_PTR;
conf->upstream.socket_keepalive = NGX_CONF_UNSET;
@@ -3444,9 +3452,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif
- /* "proxy_cyclic_temp_file" is disabled */
+ /* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
-
conf->upstream.change_buffering = 1;
conf->headers_source = NGX_CONF_UNSET_PTR;
@@ -3523,6 +3530,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
ngx_conf_merge_value(conf->upstream.force_ranges,
prev->upstream.force_ranges, 0);
+ ngx_conf_merge_value(conf->upstream.duplicate_chunked,
+ prev->upstream.duplicate_chunked, 0);
+
ngx_conf_merge_ptr_value(conf->upstream.local,
prev->upstream.local, NULL);
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
@@ -1334,10 +1334,10 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t
conf->upstream.intercept_errors = NGX_CONF_UNSET;
- /* "scgi_cyclic_temp_file" is disabled */
+ /* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
-
conf->upstream.change_buffering = 1;
+ conf->upstream.duplicate_chunked = 0;
ngx_str_set(&conf->upstream.module, "scgi");
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
@@ -1578,10 +1578,10 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
#endif
- /* "uwsgi_cyclic_temp_file" is disabled */
+ /* the hardcoded values */
conf->upstream.cyclic_temp_file = 0;
-
conf->upstream.change_buffering = 1;
+ conf->upstream.duplicate_chunked = 0;
ngx_str_set(&conf->upstream.module, "uwsgi");
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
@@ -5292,7 +5292,7 @@ ngx_http_upstream_process_transfer_encod
u = r->upstream;
- if (u->headers_in.transfer_encoding) {
+ if (u->headers_in.transfer_encoding && !u->conf->duplicate_chunked) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"upstream sent duplicate header line: \"%V: %V\", "
"previous value: \"%V: %V\"",
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
@@ -182,6 +182,7 @@ typedef struct {
ngx_flag_t intercept_errors;
ngx_flag_t cyclic_temp_file;
ngx_flag_t force_ranges;
+ ngx_flag_t duplicate_chunked;
ngx_path_t *temp_path;
More information about the nginx-devel
mailing list