Headers do much of the work in web applications. They describe properties of a request or response, control caching, handle security, and help track down problems. This article looks at how to manage request and response headers in the Angie server.
Video version
A video version of this article is available on YouTube.
Default response headers
The Angie server sets a large number of headers automatically. Some are required for the protocol to work correctly; others are optional and can be turned off.
The basic Server header shows the name of the web server and can also include its version. By default the header contains the server name and version:
curl --head localhost
...
Server: Angie/1.12.1
...
This header is controlled with the server_tokens directive. The open-source version only allows turning the version off:
server_tokens off;
You can also specify a build version (the build parameter). Angie PRO allows an arbitrary string in this header, or disabling it entirely (an empty string).
The Etag header is added by default for static assets; its behavior is covered in more detail in the article on client-side caching. You can turn it off with the etag directive:
etag off;
The standard Date header is also set, containing the server date. Content-Type deserves separate attention. It determines how the browser handles the content: display an image, render text, account for the encoding, and so on. For static files, Content-Type is determined from the file extension, according to the types file (/etc/angie/mime.types by default). If the types file is missing an entry, you can add it. If Content-Type cannot be determined, the default type is used, set by the default_type directive:
default_type application/octet-stream;
Browsers that receive this type usually offer to download the response as a file rather than render it.
In addition to the type, the Content-Type header can carry the content encoding. Encoding handling belongs to the standard Charset module. For web applications the usual approach is to set the content encoding (typically utf-8) with the charset directive:
charset utf-8;
With this in place, the encoding appears in the header:
curl --head localhost
...
Content-Type: text/html; charset=utf-8
...
The server can also convert encodings. Set the source encoding (source_charset) and the content is re-encoded to the target encoding (charset). By default, proxied responses with an encoding set are not converted, but you can enable it with:
override_charset on;
So Angie handles encodings of dynamic and static responses in a fine-grained way.
Custom response headers
Let's start with response headers set by the standard Headers module. A typical task is to add a custom header to the response:
add_header Alt-Svc 'h3=":443"; ma=86400';
The add_header directive adds a header in the context where it is specified. Inheritance follows the general rule for directives: when several directives exist at different levels, the directive from the innermost context applies, and directives from outer contexts do not. So to keep an extra header you have to repeat the add_header directive:
server {
add_header Alt-Svc 'h3=":443"; ma=86400';
location /static/ {
add_header Alt-Svc 'h3=":443"; ma=86400';
add_header x-static "Static";
}
}
The Headers module has one more directive for managing headers: expires. It sets the Expires and Cache-Control headers, which control client-side caching. For a quick "forever" cache it is enough to add:
location /static/ {
expires max;
}
Client-side caching is covered in more detail in a separate article. For more flexible header control there is the third-party Headers-More module.
The Headers-More module
The third-party Headers-More module ships with the OpenResty module set. It manages headers in a wide range of ways, including adding, changing and removing them. Full documentation is available on the project page.
Installation is standard for Angie:
apt install angie-pro-module-headers-more
It is loaded in the main configuration context:
load_module modules/ngx_http_headers_more_filter_module.so;
Start with the basic way of setting response headers:
location /static/ {
more_set_headers 'X-more1: test1' 'X-more2: test2';
}
The more_set_headers directive in the example above sets two extra headers. If the same headers are also added by the standard Headers module, the output will contain duplicates. For other headers the directive rewrites their values instead of adding a duplicate. If you specifically need to add a header, pass the -a parameter.
The module can set headers based on the content type (-t) of the response or its status code (-s). For example:
location /static/ {
more_set_headers -s 200 'X-more1: test1' 'X-more2: test2';
more_set_headers -t 'text/plain' 'Content-Type: text/foo';
}
The first directive sets the headers only for status code 200; the second sets the header Content-Type: text/foo for content of type text/plain. Conditional parameters can be combined in one directive and used more than once.
The next example removes a response header:
location /static/ {
more_clear_headers 'Content-Type';
}
When removing headers, patterns are supported in header names (the * character):
location /static/ {
more_clear_headers 'X-mo*';
}
So far we have only worked with response headers. The Headers-More module contains equivalent directives for request headers.
location /static/ {
more_set_input_headers 'X-client: test1';
more_clear_input_headers 'Accept';
}
For request headers, conditional use is only supported for the Content-Type request header (-t).
Request and response headers in the Proxy module
A large part of header handling happens while proxying requests. The standard Proxy module gives fairly fine control over request and response headers of the proxied server. Proxy setup in general is covered in a separate article; here we discuss only the header aspect.
Perhaps the most common directive for working with headers is proxy_set_header:
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
}
This example sets three headers on the request to the upstream: Host (the domain name), and X-Forwarded-For and X-Real-IP to identify the client. Depending on how the backend works, you will need one of them or all three.
As with clients, a set of headers is passed by default, while others are dropped. For example, the client's request headers are passed to the backend by default; to disable this behavior use the proxy_pass_request_headers directive:
location / {
proxy_pass http://localhost:8000;
proxy_pass_request_headers off;
proxy_pass_request_body off;
}
In this example the backend receives neither the client's request headers nor the request body (proxy_pass_request_body).
By default, the following headers from the backend response are not passed to the client: Date, Server, X-Pad and X-Accel-*. If you need some of them on the client, list them in the proxy_pass_header directive:
location / {
proxy_pass http://localhost:8000;
proxy_pass_header Date;
}
You can also block headers from the backend response (proxy_hide_header):
location / {
proxy_pass http://localhost:8000;
proxy_hide_header Expires;
}
A bit earlier we noted that backend response headers matching X-Accel-* are not passed to the client by default. They are, however, used by the Angie server for caching, redirects and encodings. Their effect can be turned off with:
location / {
proxy_pass http://localhost:8000;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
}
This configuration lets Angie cache the response to disk even when the backend response headers forbid it (for example, with the header Expires: -1).
Two more Proxy module settings define the bucket size (proxy_headers_hash_bucket_size) and the maximum hash table size (proxy_headers_hash_max_size) for the proxy_hide_header and proxy_set_header directives. Change them when errors occur while processing headers — first raise the maximum table size, then the bucket size.
The other proxying modules (FastCGI, SCGI, uWSGI) offer similar header handling.
Summary
Working with request and response headers is a core part of administering any web application. This article went through practical solutions to real problems and the capabilities of standard and third-party modules for HTTP headers.