Matching & Acting upon Headers received from Upstream with proxy_pass
Maxim Dounin
mdounin at mdounin.ru
Fri Feb 1 13:29:59 UTC 2019
Hello!
On Thu, Jan 31, 2019 at 09:06:10PM +0000, Alec Muffett wrote:
> I'm running a reverse proxy and I want to trap when upstream is sending me:
>
> Content-Encoding: gzip
>
> ...and on those occasions return (probably) 406 downstream to the client;
> the reason for this is that I am always using:
>
> proxy_set_header Accept-Encoding "identity";
>
> ...so the upstream should *never* send me gzip/etc; but sometimes it does
> so because of errors with CDN configuration and "Vary:" headers, and that
> kind of thing. I would like to make the situation more obvious and easier
> to detect.
The 406 looks wrong to me. It tells the client that the response
is not acceptable as per accept headers in the request. In your
case it is more like 500, as the problem is that your backend
returned a response it shouldn't, and your frontend thinks it's a
fatal error.
Note well that HTTP servers are allowed to return gzipped
responses even to requests with "Accept-Encoding: identify".
Quoting RFC 2616 (https://tools.ietf.org/html/rfc2616#section-10.4.7):
Note: HTTP/1.1 servers are allowed to return responses which are
not acceptable according to the accept headers sent in the
request. In some cases, this may even be preferable to sending a
406 response. User agents are encouraged to inspect the headers of
an incoming response to determine if it is acceptable.
For example, this is something that can happen when only gzipped
variants of files are available on the server. Apache can be
configured to add encoding based on the extension (so ".gz" files
will be returned with "Content-Encoding: gz", see
https://httpd.apache.org/docs/2.4/mod/mod_mime.html#addencoding),
and nginx can return gzipped variants regardless of client's
support with "gzip_static always;" (see
http://nginx.org/r/gzip_static).
> I have been trying solutions like:
>
> if ( $upstream_http_content_encoding ~ /gzip/ ) { return 406; }
>
> and:
>
> map $upstream_http_content_encoding $badness {
> br 1;
> compress 1;
> deflate 1;
> gzip 1;
> identity 0;
> default 0;
> }
> ...
> server { ...
> if ($badness) { return 406; }
>
> ...but nothing is working like I had hoped, I suspect because I do not know
> if/where to place the if-statement such that the
> upstream_http_content_encoding is both set and valid during an appropriate
> processing phase.
This is not going to work as rewrite module directives are
processed while selecting a configuration to work with (see
http://nginx.org/en/docs/http/ngx_http_rewrite_module.html).
Obviously enough this happens before the request is passed to the
upstream, and before the response is received.
> The most annoying thing is that I can see that the
> upstream_http_content_encoding variable is set to "gzip", because if I do:
>
> more_set_headers "Foo: /$upstream_http_content_encoding/";
>
> ...then I can see the "Foo: /gzip/" value on the client; but that does not
> help me do what I want.
>
> Can anyone suggest a route forward, please?
You should be able to do what you want by writing a header filter
which will check headers returned and will return an error if
these headers don't match your expectations.
--
Maxim Dounin
http://mdounin.ru/
More information about the nginx
mailing list