Connections, Sessions, Requests, Logs#

Connection processing mechanisms#

Angie supports various connection processing methods. The availability of a specific method depends on the platform being used. On platforms that support multiple methods, Angie typically selects the most efficient method automatically. However, if necessary, a connection processing method can be explicitly chosen using the use directive.

The following connection processing methods are available:

Method

Description

select

A standard method. The supporting module is built automatically on platforms that do not have more efficient methods. The --with-select_module and --without-select_module build options can be used to forcibly enable or disable the building of this module.

poll

A standard method. The supporting module is built automatically on platforms that do not have more efficient methods. The --with-poll_module and --without-poll_module build options can be used to forcibly enable or disable the building of this module.

kqueue

An efficient method available on FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, and macOS.

epoll

An efficient method available on Linux 2.6+.

/dev/poll

An efficient method available on Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+, and Tru64 UNIX 5.1A+.

eventport

The event ports method is available on Solaris 10+. (Due to known issues, using the /dev/poll method is recommended instead.)

HTTP request processing#

An HTTP request goes through a series of phases, where a specific type of processing is performed at each phase.

Post-read

The initial phase. The RealIP module is invoked during this phase.

Server-rewrite

The phase where directives from the Rewrite module, defined in a server block (but outside a location block), are processed.

Find-config

A special phase where a location is selected based on the request URI.

Rewrite

Similar to the Server-rewrite phase, but it applies to rewrite rules defined within the location block selected in the previous phase.

Post-rewrite

A special phase where the request is redirected to a new location, as in the Find-config phase, if its URI was modified during the Rewrite phase.

Preaccess

During this phase, standard Angie modules like Limit Req register their handlers.

Access

The phase where the client's authorization to make the request is verified, typically by invoking standard Angie modules such as Auth Basic.

Post-access

A special phase where the satisfy any directive is processed.

Precontent

Standard module directives, such as try_files and mirror, register their handlers during this phase.

Content

The phase where the response is usually generated. Multiple standard Angie modules register their handlers at this stage, including Index. The proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass and grpc_pass directives are also handled here.

Handlers are called sequentially until one of them produces the output.

Log

The final phase, where request logging is performed. Currently, only the Log module registers its handler at this stage for access logging.

TCP/UDP session processing#

A TCP/UDP session from a client goes through a series of phases, where a specific type of processing is performed at each phase:

Post-accept

The initial phase after accepting a client connection. The RealIP module is invoked at this phase.

Pre-access

A preliminary phase for checking access. The Set modules are invoked during this phase.

Access

The phase for limiting client access before actual data processing. The Access module is invoked at this stage.

SSL

The phase where TLS/SSL termination occurs. The SSL module is invoked during this phase.

Preread

The phase for reading initial bytes of data into the preread buffer to allow modules such as SSL Preread to analyze the data before processing.

Content

A mandatory phase where the data is actually processed, typically involving the Return module to send a response to the client. The proxy_pass directive is also handled here.

Log

The final phase where the outcome of client session processing is recorded. The Log module is invoked at this phase.

Selecting a server for a session#

Angie selects the server block for a session by the address and port that accepted it, exactly as it does for HTTP connections; see Selecting a server by address and port for the matching rules and for the default server of a pair. TCP and UDP pairs of one port are separate, each with a default server of its own: a server with listen 9000 udp; never receives the TCP connections of port 9000.

Within the matched pair, a session takes its server name from SNI alone: either the SSL module terminates TLS and reads the name from the handshake, or the SSL Preread module reads it without terminating the connection. Angie then tests that name against the server_name entries of the servers that listen on the pair; a session with no SNI name, or with a name that matches none of them, is handled by the pair's default server.

Processing requests#

Virtual server selection#

Initially, a connection is created within the context of a default server. The server name can then be determined in the following stages of request processing, each of which is involved in the selection of server configuration:

  • During the SSL handshake, in advance, according to the SNI.

  • After processing the request line.

  • After processing the Host header field.

If the server name is not determined after processing the request line or the Host header field, Angie will use an empty name as the server name.

At each of these stages, different server configurations may be applied. Therefore, certain directives should be specified with caution:

  • In the case of the ssl_protocols directive, the protocol list is set by the OpenSSL library before the server configuration is applied according to the name requested through SNI. As a result, protocols should only be specified for the default server.

  • The client_header_buffer_size and merge_slashes directives are applied before reading the request line. Therefore, these directives use either the default server configuration or the server configuration chosen by SNI.

  • In the case of the ignore_invalid_headers, large_client_header_buffers, and underscores_in_headers directives, which are involved in processing request header fields, the server configuration additionally depends on whether it was updated according to the request line or the Host header field.

  • An error response is handled using the error_page directive in the server that is currently processing the request.

Name-based virtual servers#

Angie first determines which server should handle the request. Consider a simple configuration where all three virtual servers listen on port 80:

server {

    listen 80;
    server_name example.org www.example.org;
    # ...
}

server {

    listen 80;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 80;
    server_name example.com www.example.com;
    #  ...
}

In this configuration, Angie determines which server should handle the request based solely on the Host header field. If the value of this header does not match any server name, Angie routes the request to the default server for the *:80 pair. In the configuration above, the default server is the first one — which is Angie's standard default behavior. It can also be explicitly specified which server should be the default using the default_server parameter in the listen directive:

server {

    listen 80 default_server;
    server_name example.net www.example.net;
    #  ...
}

Note

Note that the default server is a property of the address:port pair, not of the server name.

Internationalized names#

Internationalized domain names (IDNs) should be specified using an ASCII (Punycode) representation in the server_name directive:

server {

    listen 80;
    server_name xn--e1afmkfd.xn--80akhbyknj4f; # пример.испытание
    #    ...
}

Preventing requests with undefined server names#

If requests without the Host header field should not be allowed, a server that simply drops such requests can be defined:

server {

    listen 80;
    server_name "";
    return 444;
}

In this configuration, the server name is set to an empty string, which matches requests without the Host header field. A special non-standard code 444 is then returned, which closes the connection.

Selecting a server by address and port#

Angie selects the server block for a connection in two steps: first by the address and port that accepted the connection, then by name.

The listen directives of the configuration define the address:port pairs that Angie accepts connections on. Pairs on different ports, in different address families, or on different transports never compete for a connection: a quic listener is UDP, so it has a pair of its own even on a port that also carries TCP listeners. When a connection arrives, Angie matches the address that accepted it against the pairs of that port: an explicit address wins, and *:port is the last resort. A server that listens on *:port therefore has a pair of its own and does not stand in for the servers on the explicit addresses of that port. The matched pair decides the rest of the selection: which servers Angie tests by name, which of them is the default, and which of the ssl, http2, quic, and proxy_protocol parameters apply to the connection. A UNIX-domain socket path is a pair in the same sense: it has a default server and a set of server names of its own.

Angie then tests the Host header field against the server_name entries of the servers whose listen directives name the matched pair; servers listening on another address of the same port, *:port among them, are never consulted. If no name matches, the pair's default server handles the request: the first server in the configuration that names the pair, unless another server names it with the default_server parameter. A request that carries no host name is matched against the empty server name, as described under Preventing requests with undefined server names; over HTTP/2 and HTTP/3 such a request is rejected before a server is selected. Angie rejects a configuration in which two servers claim the same pair as default, reporting a duplicate default server for address:port.

On a TLS connection, Angie begins the handshake in the configuration of the pair's default server, so its ssl_certificate is the one offered unless the SNI name matches another server on the same pair; see Name-Based HTTPS Servers for serving several names on a single pair.

In the configuration below, four servers listen on port 80 — two on 198.51.100.1, one on 198.51.100.2, and one on *:80:

server {

    listen 198.51.100.1:80;
    server_name example.org www.example.org;
    #  ...
}

server {

    listen 198.51.100.1:80;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 198.51.100.2:80;
    server_name example.com www.example.com;
    #  ...
}

server {

    listen 80;
    server_name www.example.test;
    #  ...
}

A connection accepted on 198.51.100.1:80 matches that pair, so Angie tests the Host header field against the first two servers only. A request for www.example.test is therefore handled by the first of them, the default server for 198.51.100.1:80, although the fourth server carries that name; the same goes for www.example.com, which belongs to the pair 198.51.100.2:80. The fourth server handles only the connections accepted on addresses of port 80 that no other listen directive names.

Every pair has a default server of its own, and default_server designates it for the pair named by the same listen directive:

server {

    listen 198.51.100.1:80;
    server_name example.org www.example.org;
    #  ...
}

server {

    listen 198.51.100.1:80 default_server;
    server_name example.net www.example.net;
    #  ...
}

server {

    listen 198.51.100.2:80 default_server;
    server_name example.com www.example.com;
    #  ...
}

Adding listen 80 default_server; to a further server would set the default for *:80 alone; the default for 198.51.100.1:80 would remain the server that carries default_server there.

Note

The socket parameters of the listen directive, bind among them, change how Angie binds to addresses, not which server it selects: the connection is matched against the same pairs either way. See the bind parameter of listen for how the binding itself works.

Choosing locations#

Consider a simple PHP website configuration:

server {

    listen 80;
    server_name example.org www.example.org;
    root /data/www;

    location / {

        index index.html index.php;
    }

    location ~* \.(gif|jpg|png)$ {

        expires 30d;
    }

    location ~ \.php$ {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME
        $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Angie first searches for the most specific prefix location given by literal strings, regardless of their listed order. In the configuration above, the only prefix location is location /, which matches any request and will be used as a last resort. Angie then checks locations defined by regular expressions in the order they appear in the configuration file. The first matching expression stops the search, and Angie will use that location. If no regular expression matches a request, Angie will use the most specific prefix location found earlier.

Note

Locations of all types test only the URI part of the request line, excluding arguments. This is because arguments in the query string can be specified in various ways, for example:

  • /index.php?user=john&page=1

  • /index.php?page=1&user=john

Additionally, query strings may contain any number of parameters:

  • /index.php?page=1&something+else&user=john

Now let's look at how requests would be processed in the configuration above:

  • The request /logo.gif is first matched by the prefix location / and then by the regular expression .(gif|jpg|png)$. Therefore, it is handled by the latter location. Using the directive root /data/www, the request is mapped to the file /data/www/logo.gif, and the file is sent to the client.

  • The request /index.php is also initially matched by the prefix location / and then by the regular expression .(php)$. Consequently, it is handled by the latter location, and the request is passed to a FastCGI server listening on localhost:9000. The fastcgi_param directive sets the FastCGI parameter SCRIPT_FILENAME to /data/www/index.php, and the FastCGI server executes the file. The variable $document_root is set to the value of the root directive, and the variable $fastcgi_script_name is set to the request URI, i.e., /index.php.

  • The request /about.html is matched only by the prefix location /, so it is handled in this location. Using the directive root /data/www, the request is mapped to the file /data/www/about.html, and the file is sent to the client.

Handling the request / is more complex. It is matched only by the prefix location /, so it is handled by this location. The index directive then tests for the existence of index files according to its parameters and the root /data/www directive. If the file /data/www/index.html does not exist but the file /data/www/index.php does, the directive performs an internal redirect to /index.php, and Angie searches the locations again as if the request had been sent by a client. As previously mentioned, the redirected request will eventually be handled by the FastCGI server.

The examples above map a request to a file through the root directive, but the full mapping involves several directives acting together. Once a matching location is selected, Angie maps the normalized $uri of the request to a filesystem path: the root directive appends the whole URI to its value, whereas the alias directive replaces the matched location prefix. The try_files directive then probes for candidate files relative to that path; if none exist, its last parameter triggers an internal redirect, and Angie searches the locations again against the new $uri, just as with the index redirect above.

For the same request /images/top.gif, the two locations below resolve it to the same file, /data/w3/images/top.gif:

location /images/ {
  root /data/w3;           # append:  /data/w3 + /images/top.gif
}

location /images/ {
  alias /data/w3/images/;  # replace: /images/ -> /data/w3/images/, then top.gif
}

With alias, the matched prefix is removed by its length, so the trailing slashes of the location and of the alias value must be consistent. Dropping the trailing slash from the alias path above would instead yield /data/w3/imagestop.gif.

Proxying and Load Balancing#

One common use of Angie is to set it up as a proxy server. In this role, Angie receives requests, forwards them to the proxied servers, retrieves responses from those servers, and sends the responses back to the clients.

A simple proxy server:

server {

    location / {

        proxy_pass http://backend:8080;
    }

The proxy_pass directive instructs Angie to pass client requests to the backend backend:8080 (the proxied server). There are many additional directives available for further configuring a proxy connection.

FastCGI Proxying#

Angie can be used to route requests to FastCGI servers that run applications built with various frameworks and programming languages, such as PHP.

The most basic Angie configuration for working with a FastCGI server involves using the fastcgi_pass directive instead of the proxy_pass directive, along with fastcgi_param directives to set parameters passed to the FastCGI server. Suppose the FastCGI server is accessible on localhost:9000. In PHP, the SCRIPT_FILENAME parameter is used to determine the script name, and the QUERY_STRING parameter is used to pass request parameters. The resulting configuration would be:

server {

    location / {

        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING $query_string;
    }

    location ~ \.(gif|jpg|png)$ {

        root /data/images;
    }
}

This configuration sets up a server that routes all requests, except those for static images, to the proxied server operating on localhost:9000 via the FastCGI protocol.

WebSocket Proxying#

To upgrade a connection from HTTP/1.1 to WebSocket, the protocol switch mechanism available in HTTP/1.1 is used.

However, there is a subtlety: since the Upgrade header is a hop-by-hop header, it is not passed from the client to the proxied server. With forward proxying, clients may use the CONNECT method to circumvent this issue. This approach does not work with reverse proxying, as clients are unaware of any proxy servers, and special processing on the proxy server is required.

Angie implements a special mode of operation that allows setting up a tunnel between a client and a proxied server if the proxied server returns a response with code 101 (Switching Protocols), and the client requests a protocol switch via the Upgrade header in the request.

As mentioned, hop-by-hop headers, including Upgrade and Connection, are not passed from the client to the proxied server. Therefore, for the proxied server to be aware of the client's intention to switch to the WebSocket protocol, these headers must be explicitly passed:

location /chat/ {

    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

A more sophisticated example demonstrates how the value of the Connection header field in a request to the proxied server depends on the presence of the Upgrade field in the client request header:

http {

    map $http_upgrade $connection_upgrade {

        default upgrade;
        '' close;
    }

    server {

        ...

        location /chat/ {

            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
}

By default, the connection will be closed if the proxied server does not transmit any data within 60 seconds. This timeout can be increased using the proxy_read_timeout directive. Alternatively, the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still active.

Load Balancing#

Load balancing across multiple application instances is a widely used technique to optimize resource utilization, maximize throughput, reduce latency, and ensure fault-tolerant configurations.

Angie can be used as a highly efficient HTTP load balancer to distribute traffic to multiple application servers, thereby enhancing the performance, scalability, and reliability of web applications.

The simplest configuration for load balancing with Angie might look like this:

http {

    upstream myapp1 {

        server srv1.example.com;
        server srv2.example.com;
        server srv3.example.com;
    }

    server {

        listen 80;

        location / {

            proxy_pass http://myapp1;
        }
    }
}

In the example above, three instances of the same application are running on srv1 through srv3. When a load balancing method is not explicitly configured, it defaults to round-robin. Other supported load balancing mechanisms include: weight, least_conn, and ip_hash. The reverse proxy implementation in Angie also supports in-band (or passive) server health probes. These are configured using the max_fails and fail_timeout directives within the server block in the upstream context.

Logging#

Note

In addition to the options listed here, you can also enable the debugging log.

Syslog#

The error_log and access_log directives support logging to syslog. The following parameters are used to configure logging to syslog:

server=address

Specifies the address of a syslog server. The address can be a domain name or an IP address, with an optional port, or a UNIX domain socket path specified after the "unix:" prefix. If the port is not specified, UDP port 514 is used. If a domain name resolves to multiple IP addresses, the first resolved address is used.

facility=string

Sets the facility for syslog messages, as defined in RFC 3164. Possible facilities include: "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp", "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0".."local7". The default is "local7".

severity=string

Defines the severity level of syslog messages for access_log, as specified in RFC 3164. Possible values are the same as those for the second parameter (level) of the error_log directive. The default is "info". The severity of error messages is determined by Angie, so this parameter is ignored in the error_log directive.

tag=string

Sets the tag for syslog messages. The default tag is "angie".

nohostname

Disables the addition of the hostname field in the syslog message header.

Example syslog configuration:

error_log syslog:server=192.168.1.1 debug;

access_log syslog:server=unix:/var/log/angie.sock,nohostname;
access_log syslog:server=[2001:db8::1]:12345,facility=local7,tag=angie,severity=info combined;