Send original headers to the upstream

Maxim Dounin mdounin at mdounin.ru
Wed Jun 26 01:42:01 UTC 2024


Hello!

On Tue, Jun 25, 2024 at 11:22:14PM +0100, Kirill A. Korinsky wrote:

> Greetings,
> 
> Here is a patch that adds a string to proxy_pass_request_headers as a
> possible value.
> 
> Such a string is used as a prefix for headers which is overwritten by Nginx
> when it proxies the request to upstream.
> 
> As a nice side effect, this feature preserves the original order of HTTP
> headers, which might be needed sometimes.
> 
> It is only implemented on the proxy module because there is no other way to
> deliver the original request without touching it.

Could you please provide some more details about specific use 
cases for such a feature?

As far as I see, using appropriate

    proxy_set_header X-Original-Foo $http_foo;

directives should be mostly equivalent, except it won't preserve 
the headers order and will merge duplicate headers.  But these 
aren't really guaranteed by HTTP anyway, and merging/reorder can 
happen on any intermediate hosts.

> 
> diff --git src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_proxy_module.c
> index 536482ec5..6a722042b 100644
> --- src/http/modules/ngx_http_proxy_module.c
> +++ src/http/modules/ngx_http_proxy_module.c
> @@ -117,6 +117,8 @@ typedef struct {
>      ngx_uint_t                     headers_hash_max_size;
>      ngx_uint_t                     headers_hash_bucket_size;
>  
> +    ngx_str_t                      request_headers_prefix;
> +
>  #if (NGX_HTTP_SSL)
>      ngx_uint_t                     ssl;
>      ngx_uint_t                     ssl_protocols;
> @@ -215,6 +217,8 @@ static char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd,
>      void *conf);
>  static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
>      void *conf);
> +static char *ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd,
> +    void *conf);
>  #if (NGX_HTTP_CACHE)
>  static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,
>      void *conf);
> @@ -445,9 +449,9 @@ static ngx_command_t  ngx_http_proxy_commands[] = {
>  
>      { ngx_string("proxy_pass_request_headers"),
>        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
> -      ngx_conf_set_flag_slot,
> +      ngx_http_proxy_pass_request_headers,
>        NGX_HTTP_LOC_CONF_OFFSET,
> -      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers),
> +      0,
>        NULL },
>  
>      { ngx_string("proxy_pass_request_body"),
> @@ -1389,7 +1393,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
>              if (ngx_hash_find(&headers->hash, header[i].hash,
>                                header[i].lowcase_key, header[i].key.len))
>              {
> -                continue;
> +                if (plcf->request_headers_prefix.len == 0) {
> +                    continue;
> +                }
> +                len += plcf->request_headers_prefix.len;
>              }
>  
>              len += header[i].key.len + sizeof(": ") - 1
> @@ -1525,7 +1532,12 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
>              if (ngx_hash_find(&headers->hash, header[i].hash,
>                                header[i].lowcase_key, header[i].key.len))
>              {
> -                continue;
> +                if (plcf->request_headers_prefix.len == 0) {
> +                    continue;
> +                }
> +                b->last = ngx_copy(b->last,
> +                                   plcf->request_headers_prefix.data,
> +                                   plcf->request_headers_prefix.len);
>              }
>  
>              b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);

Note that as implemented, you won't be able to distinguish 
original requests headers from the ones with prefix added.  E.g., 
assuming prefix "X-Original-", and original request headers:

Host: foo
X-Original-Host: bar

you'll get

X-Original-Host: foo
X-Original-Host: bar

in the upstream request, and you won't be able to tell which one 
is real.

Following the exiting proxy_set_header behaviour, I would rather 
suggests that such a feature, if implemented, should drop all the 
headers with the configured prefix.

> @@ -3349,6 +3361,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
>       *     conf->body_values = NULL;
>       *     conf->body_source = { 0, NULL };
>       *     conf->redirects = NULL;
> +     *     conf->request_headers_prefix = { NULL, 0 };
>       *     conf->ssl = 0;
>       *     conf->ssl_protocols = 0;
>       *     conf->ssl_ciphers = { 0, NULL };
> @@ -3727,6 +3740,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
>      ngx_conf_merge_value(conf->upstream.intercept_errors,
>                                prev->upstream.intercept_errors, 0);
>  
> +    ngx_conf_merge_str_value(conf->request_headers_prefix,
> +                             prev->request_headers_prefix, "");
> +
>  #if (NGX_HTTP_SSL)
>  
>      if (ngx_http_proxy_merge_ssl(cf, conf, prev) != NGX_OK) {
> @@ -4772,6 +4788,36 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
>  }
>  
>  
> +static char *
> +ngx_http_proxy_pass_request_headers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
> +{
> +    ngx_http_proxy_loc_conf_t *plcf = conf;
> +
> +    ngx_str_t                  *value;

Style: wrong indentation of variable name, should be

    ngx_str_t  *value;

> +
> +    if (plcf->upstream.pass_request_headers != NGX_CONF_UNSET) {
> +        return "is duplicate";
> +    }
> +
> +    value = cf->args->elts;
> +
> +    if (ngx_strcmp(value[1].data, "off") == 0) {
> +        plcf->upstream.pass_request_headers = 0;
> +        return NGX_CONF_OK;
> +    }
> +
> +    plcf->upstream.store = 1;

Seems to be a cut-n-paste leftover.

> +
> +    if (ngx_strcmp(value[1].data, "on") == 0) {
> +        return NGX_CONF_OK;
> +    }
> +
> +    plcf->request_headers_prefix = value[1];

Not sure if reusing "proxy_pass_request_headers" for such a 
feature is a good idea.  From the behaviour of 
"proxy_pass_request_headers off;", I would rather assume that
"proxy_pass_request_headers X-Original-;" will pass all the 
headers with the specified prefix, and not just the headers which 
were modified.

> +
> +    return NGX_CONF_OK;
> +}
> +
> +
>  #if (NGX_HTTP_CACHE)
>  
>  static char *
> 
> -- 
> wbr, Kirill

-- 
Maxim Dounin
http://mdounin.ru/



More information about the nginx mailing list