2

I'm having a difficult time getting add_header to work when try_files is used to send requests to a named location which specifies a backend with proxy_pass.

Basically, I want to override Content-Type for a specific set of URIs, but don't want to use a map to set a variable for it (since I'd like the types and mime.types mechanism to continue working as is), nor to set an extension on the URIs and add more types declarations.

Here's a simplified version of my nginx.conf:

http {
    include       mime.types;
    default_type  text/html;
    charset       utf-8;

    server {
        listen 80;

        location @backend {
            rewrite ^ /proxy$uri break;
            proxy_pass https://backend;
            proxy_intercept_errors on;

            aws_access_key ***;
            aws_secret_key ***;
            s3_bucket ***;
            chop_prefix /proxy;

            proxy_set_header Authorization $s3_auth_token;
            proxy_set_header x-amz-date $aws_date;
            proxy_hide_header Content-Type;
        }

        location / {
            try_files false @backend;
        }

        location ~ /(textfile|anothertextfile)$ {
            try_files false @backend;
            add_header Content-Type 'text/plain' always;
            # This has no effect either
            # default_type text/plain;
        }
    }
}

I'm using ngx_aws_auth here, but I don't think that should matter.

The behavior I've seen with nginx 1.16.0 is that Content-Type is not returned at all; not for the /textfile location where I expect text/plain, nor for any other URL where I expect text/html because of the default_type at the http level. If I remove the proxy_hide_header Content-Type line then I simply get the backend's header, which is what I want to override.

I understand the frankly unintuitive behavior of add_header where headers aren't inherited from a higher level if add_header is specified at the current level, which I don't think is happening here, but I also tried moving all header directives to a standalone .conf file and including it everywhere, and I still get the same behavior.

I also tried using the headers-more module, with no difference.

What am I missing, Server Fault?

Thanks!

imiric
  • 143
  • 2
  • 6
  • 1
    Have you tried switching the line `add_header Content-Type 'text/plain' always;` to `location @backend` block? – Pothi Kalimuthu Jun 05 '19 at 04:07
  • 1
    @PothiKalimuthu That would set 'text/plain' for all backend responses, which I don't want to do. Again, I want to serve 'text/html' as default, and 'text/plain' only for a few specific URIs. In both cases this needs to override the backend's Content-Type. – imiric Jun 05 '19 at 08:52
  • Okay. Got it. It was my misunderstanding, sorry. In that case, you could resolve the issue by having the exact replica of `location @backend` block into something like `location @custom_backend` with an extra line `add_header Content-Type 'text/plain' always;` in the custom_backend block. If this is confusing, please see my answer with the sample code. – Pothi Kalimuthu Jun 06 '19 at 02:31

1 Answers1

3

The solution is to use an additional named block to have a custom header for all the requests pass through it. The code in the original question didn't work because add_header is effective only on the last matched location block. If a request passes through multiple location blocks, it doesn't pick up add_header directive on the passed location blocks. Nginx only considers or looks for add_header in the last matched location block. In this case, the named location block is the last matched location block. I hope that clarifies why the original code didn't work as expected.

http {
    include       mime.types;
    default_type  text/html;
    charset       utf-8;

    server {
        listen 80;

        location @backend {
            rewrite ^ /proxy$uri break;
            proxy_pass https://backend;
            proxy_intercept_errors on;

            aws_access_key ***;
            aws_secret_key ***;
            s3_bucket ***;
            chop_prefix /proxy;

            proxy_set_header Authorization $s3_auth_token;
            proxy_set_header x-amz-date $aws_date;
            proxy_hide_header Content-Type;
        }

        location @plain_backend {
            rewrite ^ /proxy$uri break;
            proxy_pass https://backend;
            proxy_intercept_errors on;

            aws_access_key ***;
            aws_secret_key ***;
            s3_bucket ***;
            chop_prefix /proxy;

            proxy_set_header Authorization $s3_auth_token;
            proxy_set_header x-amz-date $aws_date;
            proxy_hide_header Content-Type;

            add_header Content-Type 'text/plain' always;
        }

        location / {
            try_files false @backend;
        }

        location ~ /(textfile|anothertextfile)$ {
            try_files false @plain_backend;
        }
    }
}
Pothi Kalimuthu
  • 6,117
  • 2
  • 26
  • 38
  • 1
    Thanks, that does indeed work. I was trying to avoid duplication, but that is easily remedied with `include`. It's just unintuitive how `add_header` behaves, and ultimately I reverted to using `map` to set a custom `Content-Type` variable, since there's seemingly no way to use this named location approach while retaining the `types`/`mime.types` functionality, as [this poor fella wanted as well](https://forum.nginx.org/read.php?2,215744,215744). It's surprising to me that something so basic (overriding backend headers) is so difficult to do with nginx. Thanks again! – imiric Jun 06 '19 at 21:49
  • Good to know that you switched to map. There are indeed multiple ways to solve an issue with Nginx. None of them are clean and easy. However, with the introduction of njs, the future of Nginx customization looks promising. When njs becomes a standard module, there will be a paradigm shift on everything about Nginx. – Pothi Kalimuthu Jun 07 '19 at 00:26
  • 1
    Interesting, I hadn't heard about njs. It sounds like Lua/OpenResty, and FWIW what I wanted to do can be accomplished with Lua, according to [this comment](https://github.com/openresty/lua-nginx-module/issues/1456#issuecomment-460452159), though I haven't tried it. I don't want to add that kind of extensibility/complexity when nginx should be capable of doing it OOB. – imiric Jun 07 '19 at 10:37