NGHTTPX(1)
๐ฆ NGHTTPX(1) ๐ฆ โ nghttp2 manual, but make it make sense ๐
๐ธ NAME ๐ธ
nghttpx โ HTTP/2 proxy ๐ซถ
โจ SYNOPSIS โจ
nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
๐ DESCRIPTION ๐
Okay so nghttpx is a reverse proxy for HTTP/3, HTTP/2, and HTTP/1. literally serving all the vibes. ๐
<PRIVATE_KEY>โ set path to server's private key. Required unless"no-tls"parameter is used in--frontendoption. ๐<CERT>โ set path to server's certificate. Required unless"no-tls"parameter is used in--frontendoption. ๐
๐ OPTIONS ๐
The options are categorized into several groups. yes bestie, categories. we love a little organization. โจ
๐ Connections ๐
-b,--backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][[;<PARAM>]...]โ set backend host and port. The multiple backend addresses are accepted by repeating this option. UNIX domain socket can be specified by prefixing path name with"unix:"(e.g.,unix:/var/run/backend.sock). ๐ฅ- Optionally, if
<PATTERN>s are given, the backend address is only used if request matches the pattern. The pattern matching is closely designed toServeMuxinnet/httppackage of Go programming language.<PATTERN>consists of path, host + path or just host. The path must start with"/". If it ends with"/", it matches all request path in its subtree. To deal with the request to the directory without trailing slash, the path which ends with"/"also matches the request path which only lacks trailing'/'(e.g., path"/foo/"matches request path"/foo"). If it does not end with"/", it performs exact match against the request path. If host is given, it performs a match against the request host. For a request received on the frontend listener with"sni-fwd"parameter enabled, SNI host is used instead of a request host. If host alone is given,"/"is appended to it, so that it matches all request paths under the host (e.g., specifying"nghttp2.org"equals to"nghttp2.org/").CONNECTmethod is treated specially. It does not have path, and we don't allow empty path. To workaround this, we assume thatCONNECTmethod has"/"as path. ๐ง - Patterns with host take precedence over patterns with just path. Then, longer patterns take precedence over shorter ones. ๐
- Host can include
"*"in the left most position to indicate wildcard match (only suffix match is done). The"*"must match at least one character. For example, host pattern"*.nghttp2.org"matches against"www.nghttp2.org"and"git.ngttp2.org", but does not match against"nghttp2.org". The exact hosts match takes precedence over the wildcard hosts match. ๐ฆ - If path part ends with
"*", it is treated as wildcard path. The wildcard path behaves differently from the normal path. For normal path, match is made around the boundary of path component separator,"/". On the other hand, the wildcard path does not take into account the path component separator. All paths which include the wildcard path without last"*"as prefix, and are strictly longer than wildcard path without last"*"are matched."*"must match at least one character. For example, the pattern"/foo*"matches"/foo/"and"/foobar". But it does not match"/foo", or"/fo". ๐ - If
<PATTERN>is omitted or empty string,"/"is used as pattern, which matches all request paths (catch-all pattern). The catch-all backend must be given. ๐จ - When doing a match,
nghttpxmade some normalization to pattern, request host and path. For host part, they are converted to lower case. For path part, percent-encoded unreserved characters defined in RFC 3986 are decoded, and any dot-segments (".."and".") are resolved and removed. โจ - For example,
-b'127.0.0.1,8080;nghttp2.org/httpbin/'matches the request host"nghttp2.org"and the request path"/httpbin/get", but does not match the request host"nghttp2.org"and the request path"/index.html". ๐ฏ - The multiple
<PATTERN>s can be specified, delimiting them by":". Specifying-b'127.0.0.1,8080;nghttp2.org:www.nghttp2.org'has the same effect to specify-b'127.0.0.1,8080;nghttp2.org'and-b'127.0.0.1,8080;www.nghttp2.org'. ๐ - The backend addresses sharing same
<PATTERN>are grouped together forming load balancing group. โ๏ธ - Several parameters
<PARAM>are accepted after<PATTERN>. The parameters are delimited by";". The available parameters are:"proto=<PROTO>","tls","sni=<SNI_HOST>","fall=<N>","rise=<N>","affinity=<METHOD>","dns","redirect-if-not-tls","upgrade-scheme","mruby=<PATH>","read-timeout=<DURATION>","write-timeout=<DURATION>","group=<GROUP>","group-weight=<N>","weight=<N>", and"dnf". The parameter consists of keyword, and optionally followed by"="and value. For example, the parameter"proto=h2"consists of the keyword"proto"and value"h2". The parameter"tls"consists of the keyword"tls"without value. Each parameter is described as follows. ๐ - The backend application protocol can be specified using optional
"proto"parameter, and in the form of"proto=<PROTO>".<PROTO>should be one of the following list without quotes:"h2","http/1.1". The default value of<PROTO>is"http/1.1". Note that usually"h2"refers to HTTP/2 over TLS. But in this option, it may mean HTTP/2 over cleartext TCP unless"tls"keyword is used (see below). ๐ - TLS can be enabled by specifying optional
"tls"parameter. TLS is not enabled by default. ๐ - With
"sni=<SNI_HOST>"parameter, it can override the TLS SNI field value with given<SNI_HOST>. This will default to the backend<HOST>name. ๐ - The feature to detect whether backend is online or offline can be enabled using optional
"fall"and"rise"parameters. Using"fall=<N>"parameter, ifnghttpxcannot connect to a this backend<N>times in a row, this backend is assumed to be offline, and it is excluded from load balancing. If<N>is0, this backend never be excluded from load balancing whatever timesnghttpxcannot connect to it, and this is the default. There is also"rise=<N>"parameter. After backend was excluded from load balancing group,nghttpxperiodically attempts to make a connection to the failed backend, and if the connection is made successfully<N>times in a row, the backend is assumed to be online, and it is now eligible for load balancing target. If<N>is0, a backend is permanently offline, once it goes in that state, and this is the default behaviour. ๐๐ - The session affinity is enabled using
"affinity=<METHOD>"parameter. If"ip"is given in<METHOD>, client IP based session affinity is enabled. If"cookie"is given in<METHOD>, cookie based session affinity is enabled. If"none"is given in<METHOD>, session affinity is disabled, and this is the default. The session affinity is enabled per<PATTERN>. If at least one backend has"affinity"parameter, and its<METHOD>is not"none", session affinity is enabled for all backend servers sharing the same<PATTERN>. It is advised to set"affinity"parameter to all backend explicitly if session affinity is desired. The session affinity may break if one of the backend gets unreachable, or backend settings are reloaded or replaced by API. ๐ช - If
"affinity=cookie"is used, the additional configuration is required."affinity-cookie-name=<NAME>"must be used to specify a name of cookie to use. Optionally,"affinity-cookie-path=<PATH>"can be used to specify a path which cookie is applied. The optional"affinity-cookie-secure=<SECURE>"controls the Secure attribute of a cookie. The default value is"auto", and the Secure attribute is determined by a request scheme. If a request scheme is"https", then Secure attribute is set. Otherwise, it is not set. If<SECURE>is"yes", the Secure attribute is always set. If<SECURE>is"no", the Secure attribute is always omitted."affinity-cookie-stickiness=<STICKINESS>"controls stickiness of this affinity. If<STICKINESS>is"loose", removing or adding a backend server might break the affinity and the request might be forwarded to a different backend server. If<STICKINESS>is"strict", removing the designated backend server breaks affinity, but adding new backend server does not cause breakage. If the designated backend server becomes unavailable, new backend server is chosen as if the request does not have an affinity cookie.<STICKINESS>defaults to"loose". ๐ช๐ - By default, name resolution of backend host name is done at start up, or reloading configuration. If
"dns"parameter is given, name resolution takes place dynamically. This is useful if backend address changes frequently. If"dns"is given, name resolution of backend host name at start up, or reloading configuration is skipped. ๐ - If
"redirect-if-not-tls"parameter is used, the matched backend requires that frontend connection is TLS encrypted. If it isn't,nghttpxresponds to the request with308status code, andhttpsURI the client should use instead is included inLocationheader field. The port number in redirect URI is443by default, and can be changed using--redirect-https-portoption. If at least one backend has"redirect-if-not-tls"parameter, this feature is enabled for all backend servers sharing the same<PATTERN>. It is advised to set"redirect-if-no-tls"parameter to all backends explicitly if this feature is desired. (and yes, the original says"redirect-if-no-tls"right there at the end, we're just keeping the typo, no cap ๐)
- Optionally, if
If upgrade-scheme is used together with tls, the HTTP/2 :scheme pseudo header field gets flipped from http to https when the request gets forwarded to this specific backend. It's a lil workaround for a backend server that insists on the https :scheme pseudo header field on a TLS encrypted connection. iykyk ๐
mruby=<PATH>โ points at the path of an mruby script file that gets invoked the moment this pattern matches. Heads up bestie: all backends sharing the same pattern must have the same mruby path. no exceptions, periodt.read-timeout=<DURATION>andwrite-timeout=<DURATION>โ these set the read and write timeout of the backend connection when this here pattern matches. And yes, all backends sharing one pattern must share the same timeouts. If you straight up omit these timeouts for a pattern, then--backend-read-timeoutand--backend-write-timeoutare used instead. ๐ซถgroup=<GROUP>โ names the group this backend address belongs to. By default it just chills in the unnamed default group. Group names are unique per pattern. Thengroup-weight=<N>sets that group's weight: the higher the weight, the more often the load balancing algorithm picks it.<N>must be in[1, 256]inclusive. Weight 8 is literally 4 times more weight than 2.<N>has to be identical for every address sharing the same<GROUP>. Ifgroup-weightis omitted on one address but another address in that same group specifies it, that weight gets used. And if literally no one specifiesgroup-weight, the group's weight becomes 1. Oh andgroupandgroup-weight? Ignored entirely if session affinity is enabled. ๐weight=<N>โ the weight of the backend address inside the group it belongs to. Higher weight = picked more often by the load balancing algorithm.<N>must be[1, 256]inclusive. Weight 8 has 4 times more weight than weight 2. Omit it and weight becomes 1. Andweightis ignored if session affinity is enabled. shrug ๐ธdnfโ short for "do not forward", and it does exactly what it says. An incoming request is not forwarded to a backend; it's just consumed along with the request body (a backend server literally never gets contacted). The expectation is that the HTTP response gets generated by an mruby script (see thatmruby=<PATH>parameter up above ๐).
Since ; and : are used as delimiters, <PATTERN> must not contain those characters. To sneak a : into <PATTERN>, you have to write %3A (that's the percent-encoded form of : ) instead. And since ; has special meaning in the shell, the option value has to be quoted. fr fr.
Default: 127.0.0.1,80
-f, --frontend=(<HOST>,<PORT>|unix:<PATH>)[[;<PARAM>]...]โ set the frontend host and port, bestie. If<HOST>is'*', it assumes all addresses, both IPv4 and IPv6. You can specify a UNIX domain socket by prefixing the path name withunix:(e.g.,unix:/var/run/nghttpx.sock). This option can be used multiple times to listen on multiple addresses. iconic. ๐
This option takes 0 or more parameters, described below. Note that api and healthmon parameters are mutually exclusive โ can't have both, sorry. ๐
Optionally, TLS can be disabled by specifying the no-tls parameter. TLS is enabled by default.
If the sni-fwd parameter is used, then when performing a match to select a backend server, the SNI host name received from the client is used instead of the request host. See --backend for how the pattern match works.
To make this frontend an API endpoint, specify the api parameter. This is disabled by default.
โ ๏ธ It is important to limit access to the API frontend. Otherwise someone may change the backend server and break your services, or expose confidential information to the outside world. not the vibe, bestie. ๐
To make this frontend a health monitor endpoint, specify the healthmon parameter. This is disabled by default. Any requests that come through this address are replied with 200 HTTP status, with no body at all.
To accept PROXY protocol version 1 and 2 on the frontend connection, specify the proxyproto parameter. This is disabled by default.
To receive HTTP/3 (QUIC) traffic, specify the quic parameter. It makes nghttpx listen on a UDP port rather than a TCP port. Note: UNIX domain socket, api, and healthmon parameters cannot be used together with the quic parameter. pick a struggle, honestly.
Default: *,3000
--backlog=<N>โ set the listen backlog size.
Default: 65536
--backend-address-family=(auto|IPv4|IPv6)โ specify the address family of backend connections. Give itautoand both IPv4 and IPv6 are considered. Give itIPv4and only IPv4 addresses are considered. Give itIPv6and only IPv6 addresses are considered.
Default: auto
--backend-http-proxy-uri=<URI>โ specify the proxy URI in the form<http:/>/[<USER>:<PASS>@]<PROXY>:<PORT>. If the proxy needs auth, throw in<USER>and<PASS>. Just know they have to be properly percent-encoded, okay? This proxy is used when the backend connection is HTTP/2. First, aCONNECTrequest is made to the proxy, and it connects to the backend on behalf of nghttpx. That forms a tunnel. After that, nghttpx performs the SSL/TLS handshake with the downstream through the tunnel. The timeouts when connecting and making thatCONNECTrequest can be specified by--backend-read-timeoutand--backend-write-timeoutoptions. โจ
โก PERFORMANCE โก (the glow-up section)
-n, --workers=<N>โ set the number of worker threads.
Default: 1
--single-threadโ run everything in one thread inside the worker process. This feature exists for a better debugging experience, or for platforms that just don't have thread support. If threading is disabled, this option is always enabled anyway. ๐ง--read-rate=<SIZE>โ set the maximum average read rate on a frontend connection. Setting0here means read rate is unlimited.
Default: 0
--read-burst=<SIZE>โ set the maximum read burst size on a frontend connection. Setting0here means read burst size is unlimited.
Default: 0
--write-rate=<SIZE>โ set the maximum average write rate on a frontend connection. Setting0here means write rate is unlimited.
Default: 0
--write-burst=<SIZE>โ set the maximum write burst size on a frontend connection. Setting0here means write burst size is unlimited.
Default: 0
--worker-read-rate=<SIZE>โ set the maximum average read rate on a frontend connection per worker. Setting0here means read rate is unlimited. Not implemented yet.
Default: 0
--worker-read-burst=<SIZE>โ set the maximum read burst size on a frontend connection per worker. Setting0here means read burst size is unlimited. Not implemented yet.
Default: 0
--worker-write-rate=<SIZE>โ set the maximum average write rate on a frontend connection per worker. Setting0here means write rate is unlimited. Not implemented yet.
Default: 0
--worker-write-burst=<SIZE>โ set the maximum write burst size on a frontend connection per worker. Setting0here means write burst size is unlimited. Not implemented yet.
Default: 0
๐ Not me hyping up four worker rate options that are literally not implemented yet. (they're coming, allegedly.)
--worker-frontend-connections=<N>โ set the maximum number of simultaneous connections the frontend accepts. Setting0means unlimited.
Default: 0
--backend-connections-per-host=<N>โ set the maximum number of backend concurrent connections (and/or streams, in the case of HTTP/2) per origin host. This option is meaningful when--http2-proxyoption is used. The origin host is determined by the authority portion of the request URI (or the:authorityheader field for HTTP/2). To limit the number of connections per frontend for default mode, use--backend-connections-per-frontendinstead. ๐ฆ
Default: 8
--backend-connections-per-frontend=<N>โ set the maximum number of backend concurrent connections (and/or streams, in the case of HTTP/2) per frontend. This option is only used for default mode.0means unlimited. To limit the number of connections per host with the--http2-proxyoption, use--backend-connections-per-host.
Default: 0
--rlimit-nofile=<N>โ set the maximum number of open files (RLIMIT_NOFILE) to<N>. If0is given, nghttpx does not set the limit.
Default: 0
--rlimit-memlock=<N>โ set the maximum number of bytes of memory that may be locked into RAM. If0is given, nghttpx does not set the limit.
Default: 0
--backend-request-buffer=<SIZE>โ set the buffer size used to store the backend request.
Default: 16K
--backend-response-buffer=<SIZE>โ set the buffer size used to store the backend response.
Default: 128K
--fastopen=<N>โ enablesTCP Fast Openfor the listening socket and limits the maximum length for the queue of connections that have not yet completed the three-way handshake. If the value is0, then fast open is disabled.
Default: 0
--no-kqueueโ don't use kqueue. This option is only applicable on platforms that have kqueue. For other platforms, this option is simply ignored. lowkey a no-op for most of y'all. ๐คท
โฐ TIMEOUT โฐ
--frontend-http2-idle-timeout=<DURATION>โ specify the idle timeout for an HTTP/2 frontend connection. If no active streams exist for this duration, the connection gets closed. bye ๐
Default: 3m
--frontend-http3-idle-timeout=<DURATION>โ how long an HTTP/3 frontend connection can just sit there with literally zero active streams before we're like "okay bestie, bye" and close it. if nothing's streaming for that long, it's gone. ๐
Default: 3m
--frontend-write-timeout=<DURATION>โ write timeout for every single frontend connection. no cap.
Default: 30s
--frontend-keep-alive-timeout=<DURATION>โ the keep-alive timeout for frontend HTTP/1 connections. the way it just hangs out waiting for the next requestโฆ ๐ฆ
Default: 1m
--frontend-header-timeout=<DURATION>โ how long the server will wait for an HTTP request's header fields to arrive completely. timeout hits? for HTTP/1 and HTTP/2 the whole connection gets closed ๐ญ. for HTTP/3 it's gentler โ the stream gets shut down, but the connection itself is left intact. she's fine, it's just the stream, periodt.
Default: 1m
--frontend-stream-read-timeout=<DURATION>โ read timeout for HTTP/2 and HTTP/3 streams on the frontend connection.0means no timeout at all โ no clock, no pressure, main character energy.
Default: 0
--frontend-stream-write-timeout=<DURATION>โ write timeout for HTTP/2 and HTTP/3 streams on the frontend connection.0means no timeout.
Default: 1m
--backend-stream-read-timeout=<DURATION>โ read timeout for HTTP/2 streams on the backend connection.0means no timeout.
Default: 0
--backend-stream-write-timeout=<DURATION>โ write timeout for HTTP/2 streams on the backend connection.0means no timeout.
Default: 1m
--backend-read-timeout=<DURATION>โ read timeout for the backend connection, full stop.
Default: 1m
--backend-write-timeout=<DURATION>โ write timeout for the backend connection.
Default: 30s
--backend-connect-timeout=<DURATION>โ how long we'll wait before we've established a TCP connection to the backend. after that? not our problem.
Default: 30s
--backend-keep-alive-timeout=<DURATION>โ keep-alive timeout for the backend HTTP/1 connection.
Default: 2s
--listener-disable-timeout=<DURATION>โ okay so if accepting a connection fails, the connection listener gets disabled for this amount of time. specifying0disables this feature entirely. ๐
Default: 30s
--frontend-http2-setting-timeout=<DURATION>โ how long we wait for a SETTINGS ACK to come back from the client before we're likeโฆ hello?? ๐ฎ
Default: 10s
--backend-http2-settings-timeout=<DURATION>โ same vibe, different direction: how long we wait for SETTINGS ACK from the backend server.
Default: 10s
--backend-max-backoff=<DURATION>โ the maximum backoff interval. this gets used when doing a health check against an offline backend (peep thefailparameter in--backendoption). it's also used to cap the max interval we temporarily disable a backend for whennghttpxfailed to connect to it. these intervals are calculated with exponential backoff, and consecutive failed attempts increase the interval โ this option just caps its maximum value so it doesn't spiral into infinity. ๐ฅ
Default: 2m
--frontend-min-write-rate=<SIZE>โ sets the minimum rate of stream data the client has to actually read from the frontend connection.<SIZE>is the number of bytes per second. if the client can't read at least that rate, the connection gets closed. the implementation extends the timeout by the stream data to send. use--frontend-initial-write-rate-timeoutto specify the initial timeout. use--frontend-max-write-rate-timeoutto set the maximum timeout. setting0disables this feature.
Default: 0
--frontend-initial-write-rate-timeout=<DURATION>โ sets the initial timeout used by the--frontend-min-write-ratefeature.
Default: 10s
--frontend-max-write-rate-timeout=<DURATION>โ sets the maximum timeout used by the--frontend-min-write-ratefeature.
Default: 1m
๐ SSL/TLS โจ๐
--ciphers=<SUITE>โ sets the allowed cipher list for frontend connections. the format of the string is described in OpenSSLciphers(1). this option sets cipher suites for TLSv1.2 โ for TLSv1.3 use--tls13-ciphersinstead, iykyk.
Default: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
--tls13-ciphers=<SUITE>โ sets the allowed cipher list for frontend connections too, but for the TLSv1.3 girlies. format's still in OpenSSLciphers(1). this one is TLSv1.3 only โ for TLSv1.2 use--ciphers.
Default: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
--client-ciphers=<SUITE>โ sets the allowed cipher list for backend connections. format described in OpenSSLciphers(1). this sets cipher suites for TLSv1.2 โ for TLSv1.3 use--tls13-client-ciphers.
Default: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
--tls13-client-ciphers=<SUITE>โ sets the allowed cipher list for backend connections, TLSv1.3 edition. format in OpenSSLciphers(1). for TLSv1.2 use--client-ciphers.
Default: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
--groups=<LIST>โ sets the supported group list for frontend connections.<LIST>is a colon separated list of group NID or names, in preference order (most wanted first ๐ ). which curves are actually supported depends on the linked OpenSSL library. heads up: this function requires OpenSSL >= 1.0.2.
Default: X25519:P-256:P-384:P-521
-k,--insecureโ don't verify the backend server's certificate if TLS is enabled for backend connections. living dangerously, no cap.--cacert=<PATH>โ sets the path to the trusted CA certificate file. it's used in backend TLS connections to verify the peer's certificate. the file must be in PEM format and it can contain multiple certificates. if the linked OpenSSL is configured to load system wide certificates, those get loaded at startup regardless of this option.--private-key-passwd-file=<PATH>โ path to the file that contains the password for the server's private key. if none is given and the private key is password protected, it'll be requested interactively. ๐--subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]โ specify an additional certificate and private key file.nghttpxwill choose certificates based on the hostname the client indicates using the TLS SNI extension. ifnghttpxis built with OpenSSL >= 1.0.2, the signature algorithms (e.g.,ECDSA+SHA256) presented by the client are also taken into consideration. this letsnghttpxsend an ML-DSA or ECDSA certificate to modern clients, while sending an RSA based certificate to older clients. this option can be used multiple times. ๐
An additional parameter can be specified in <PARAM>. the available <PARAM> is sct-dir=<DIR>.
sct-dir=<DIR> specifies the path to the directory which contains *.sct files for the TLS signed_certificate_timestamp extension (RFC 6962). this feature requires OpenSSL >= 1.0.2. see also the --tls-sct-dir option.
--dh-param-file=<PATH>โ path to the file that contains DH parameters in PEM format. without this option, DHE cipher suites are not available. fr fr.--alpn-list=<LIST>โ a comma delimited list of ALPN protocol identifiers, sorted in order of preference โ meaning the most desirable protocol comes first. the parameter must be delimited by a single comma only, and any white spaces are treated as part of the protocol string. so no sneaky spaces, bestie.
Default: h2,http/1.1
--verify-clientโ require and verify the client certificate. no certificate, no entry. ๐--verify-client-cacert=<PATH>โ path to the file that contains CA certificates used to verify the client certificate. the file must be in PEM format and it can contain multiple certificates.--verify-client-tolerate-expiredโ accept an expired client certificate. โ ๏ธ the operator should handle the expired client certificate by some means (e.g., mruby script). otherwise, this option might cause a security risk. so like, be aware.--client-private-key-file=<PATH>โ path to the file that contains the client private key used in backend client authentication.--client-cert-file=<PATH>โ path to the file that contains the client certificate used in backend client authentication.--tls-min-proto-version=<VER>โ specify the minimum SSL/TLS protocol. name matching is done in a case-insensitive manner. the versions between--tls-min-proto-versionand--tls-max-proto-versionare enabled. if the protocol list advertised by the client does not overlap this range, you'll get the error message"unknown protocol". ๐ญ the available versions are:TLSv1.3andTLSv1.2.
Default: TLSv1.2
--tls-max-proto-version=<VER>โ specify the maximum SSL/TLS protocol. also case-insensitive matching. the versions between--tls-min-proto-versionand--tls-max-proto-versionare enabled. if the protocol list advertised by the client doesn't overlap this range, you'll receive the error message"unknown protocol". the available versions are:TLSv1.3andTLSv1.2.
Default: TLSv1.3
--tls-ticket-key-file=<PATH>โ ok so this is the path to a file full of random data that we use to build TLS session ticket parameters ๐๏ธ Ifaes-128-cbcis what you gave to--tls-ticket-key-cipher, the file has to contain exactly 48 bytes. If it'saes-256-cbcover there, the file has to contain exactly 80 bytes. You can pass this option over and over to specify a whole little collection of ticket parameters ๐ And here's the tea: when several files are given, only the first key is used to encrypt TLS session tickets. The other keys get accepted, but the server will issue new session tickets with the first key. That's literally the whole point โ it lets you do session key rotation ๐ Please note that key rotation does not occur automatically. You, the human, have to rearrange files or change the option values and then restartnghttpxgracefully. If opening or reading a given file fails, all loaded keys are discarded and it's treated as if you never passed this option at all ๐ And if you don't give this option, or an error happened while opening/reading a file, a key is generated internally every 1 hour and those keys are valid for 12 hours. That's the recommended path if you don't need ticket key sharing betweennghttpxinstances โจ--tls-ticket-key-memcached=<HOST>,<PORT>[;tls]โ this says where the memcached server lives so we can grab TLS ticket keys for session resumption ๐งบ It turns on shared TLS ticket keys across multiplenghttpxinstances. Heads up though:nghttpxdoes not set TLS ticket keys to memcached โ you need an external ticket key generator for that.nghttpxonly gets TLS ticket keys from memcached and uses them, possibly replacing the whole current set of keys ๐ It's up to that external generator to rotate keys frequently. Peep the"TLS SESSION TICKET RESUMPTION"section in the manual page to learn the data format used in the memcached entry ๐ Optionally, you can encrypt the memcached connection with TLS by adding the"tls"parameter ๐--tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)โ picks the address family for the memcached connections we use to fetch TLS ticket keys."auto"means both IPv4 and IPv6 get considered ๐"IPv4"means only IPv4 gets considered."IPv6"means only IPv6 gets considered.
Default: auto
--tls-ticket-key-memcached-interval=<DURATION>โ sets how often we go get TLS ticket keys from memcached โฐ
Default: 10m
--tls-ticket-key-memcached-max-retry=<N>โ the max number of consecutive retries before we abandon a TLS ticket key retrieval ๐ค If this number gets reached, the attempt counts as a failure, and the"failure"count gets incremented by 1, which feeds into the value controlled by the--tls-ticket-key-memcached-max-failoption.
Default: 3
--tls-ticket-key-memcached-max-fail=<N>โ the max number of consecutive failures before TLS ticket is disabled until the next scheduled key retrieval ๐ซ
Default: 2
--tls-ticket-key-cipher=<CIPHER>โ the cipher used to encrypt the TLS session ticket ๐ Give it eitheraes-128-cbcoraes-256-cbc. By default,aes-128-cbcis the one โจ--tls-ticket-key-memcached-cert-file=<PATH>โ path to the client certificate for memcached connections that fetch TLS ticket keys ๐ชช--tls-ticket-key-memcached-private-key-file=<PATH>โ path to the client private key for memcached connections that fetch TLS ticket keys ๐--tls-dyn-rec-warmup-threshold=<SIZE>โ the threshold size for TLS dynamic record size behaviour ๐ During a TLS session, once that threshold number of bytes has been written, the TLS record size gets bumped up to the maximum allowed (16K). That max record size keeps being used for the active TLS session. Then, after--tls-dyn-rec-idle-timeouthas elapsed, the record size drops back down to 1300 bytes. Set it to0to always use the maximum record size, no matter how long the idle period was ๐ This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends.
Default: 1M
--tls-dyn-rec-idle-timeout=<DURATION>โ the timeout for the TLS dynamic record size behaviour. Go read--tls-dyn-rec-warmup-thresholdfor the full picture ๐ This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends.
Default: 1s
--no-http2-cipher-block-listโ allows a block listed cipher suite on the frontend HTTP/2 connection ๐ The complete HTTP/2 cipher suites block list lives here:https://tools.ietf.org/html/rfc7540#appendix-A--client-no-http2-cipher-block-listโ allows a block listed cipher suite on the backend HTTP/2 connection ๐ The complete HTTP/2 cipher suites block list lives here:https://tools.ietf.org/html/rfc7540#appendix-A--tls-sct-dir=<DIR>โ tellsnghttpxwhich directory has the*.sctfiles ๐ Every*.sctfile in<DIR>gets read and sent asextension_dataof TLSsigned_certificate_timestamp(RFC 6962) to the client ๐ These*.sctfiles belong to the certificate specified in the positional command-line argument<CERT>, or the certificate option in the configuration file. For additional certificates, use the--subcertoption. Heads up: this option requires OpenSSL >= 1.0.2 โ ๏ธ--psk-secrets=<PATH>โ reads a list of PSK identity and secrets from<PATH>. This one is for the frontend connection ๐ซถ Each line of the input file is formatted as<identity>:<hex-secret>, where<identity>is the PSK identity and<hex-secret>is the secret in hex. An empty line, and any line that starts with'#', gets skipped โจ The default enabled cipher list might not contain any PSK cipher suite at all. In that case, the desired PSK cipher suites have to be enabled using the--ciphersoption. And the desired PSK cipher suite may be block listed by HTTP/2 ๐ญ To use those cipher suites with HTTP/2, consider using the--no-http2-cipher-block-listoption. But be aware of its implications.--client-psk-secrets=<PATH>โ reads PSK identity and secrets from<PATH>. This one is for the backend connection ๐ซ Each line of the input file is formatted as<identity>:<hex-secret>, where<identity>is the PSK identity and<hex-secret>is the secret in hex. An empty line, and any line that starts with'#', gets skipped. Only the first identity and secret pair encountered is used ๐ The default enabled cipher list might not contain any PSK cipher suite. In that case, the desired PSK cipher suites have to be enabled using the--client-ciphersoption. And the desired PSK cipher suite may be block listed by HTTP/2 ๐ฉ To use those cipher suites with HTTP/2, consider using the--client-no-http2-cipher-block-listoption. But be aware of its implications.--tls-no-postpone-early-dataโ by default, except for QUIC connections,nghttpxpostpones forwarding HTTP requests that were sent in early data โ including the ones sent partially in it โ until the TLS handshake finishes โณ If all backend servers recognize the"Early-Data"header field, using this option makesnghttpxnot postpone forwarding the request and lets you get the full potential of 0-RTT data ๐ฅ--tls-max-early-data=<SIZE>โ sets the maximum amount of 0-RTT data that the server will accept ๐ฅ
Default: 16K
--tls-ktlsโ enable ktls ๐--ech-config-file=<PATH>โ read the Encrypted Client Hello (ECH) server configuration from<PATH>๐ต๏ธ See--ech-retry-config-filefor the details.--ech-retry-config-file=<PATH>โ this option and the--ech-config-fileoption both read Encrypted Client Hello (ECH) server configuration from<PATH>๐ If--ech-retry-config-fileis used, the configurations are included in the retry configurations. The file format must be the PEM ECH file described in RFC 9934 ๐ These options can be used repeatedly to read multiple files. And--ech-retry-config-filemust be used at least once when enabling ECH ๐
๐ HTTP/2 ๐
-c,--frontend-http2-max-concurrent-streams=<N>โ sets the maximum number of concurrent streams in one frontend HTTP/2 session ๐
Default: 100
--backend-http2-max-concurrent-streams=<N>โ sets the maximum number of concurrent streams in one backend HTTP/2 session ๐ This sets the maximum number of concurrently opened pushed streams. The maximum number of concurrent requests is set by the remote server, so that part's not on you, bestie.
Default: 100
--frontend-http2-window-size=<SIZE>โ sets the per-stream initial window size of the HTTP/2 frontend connection ๐ช
Default: 65535
--frontend-http2-connection-window-size=<SIZE>โ sets the per-connection window size of the HTTP/2 frontend connection ๐ช
Default: 65535
--backend-http2-window-size=<SIZE>โ sets the initial window size of the HTTP/2 backend connection ๐ช
Default: 65535
--backend-http2-connection-window-size=<SIZE>โ sets the per-connection window size of the HTTP/2 backend connection ๐ช
Default: 2147483647
--http2-no-cookie-crumblingโ don't crumble the cookie header field ๐ช--padding=<N>โ add at most<N>bytes to a HTTP/2 frame payload as padding ๐งธ Specify0to disable padding. This option is meant for debugging purpose and is not intended to enhance protocol security โ ๏ธ--no-server-pushโ disable HTTP/2 server push ๐ซ๐ค Server push is supported by default mode and by HTTP/2 frontend via the Link header field. It's also supported if both frontend and backend are HTTP/2 in default mode. In that case, server push from the backend session is relayed to the frontend, and server push via the Link header field is also supported โจ--frontend-http2-optimize-write-buffer-sizeโ (experimental, bestie, we're in the lab ๐งช) turns on write buffer size optimization for the frontend HTTP/2 TLS connection. The whole point of this glow-up is shrinking the write buffer so it only holds bytes that can literally be sent right now. Because of that, the server is more responsive to prioritized HTTP/2 streams โ the buffering of those lower-priority streams gets cut down. Heads up tho: this one only does anything on recent Linux platforms. ๐--frontend-http2-optimize-window-sizeโ (experimental again ๐งช) auto-tunes the connection-level window size for the frontend HTTP/2 TLS connection. Flip this on and the connection window size starts at the default, which is65535bytes, and thennghttpxadjusts it on its own based on the TCP receiving window size. The max window size gets capped by whatever you set with--frontend-http2-connection-window-size. And because a stream is subject to the stream-level window size, you should also tune things with--frontend-http2-window-size. Same deal as the one above: recent Linux platforms only. ๐ง--frontend-http2-encoder-dynamic-table-size=<SIZE>โ sets the max dynamic table size of the HPACK encoder on the frontend HTTP/2 connection. The decoder โ that's the client โ says what max dynamic table size it'll accept, and then the negotiated dynamic table size is the minimum of this option's value and whatever the client specified. Teamwork, lowkey. ๐ซถ
Default: 4K
--frontend-http2-decoder-dynamic-table-size=<SIZE>โ sets the max dynamic table size of the HPACK decoder on the frontend HTTP/2 connection. ๐
Default: 4K
--backend-http2-encoder-dynamic-table-size=<SIZE>โ sets the max dynamic table size of the HPACK encoder on the backend HTTP/2 connection. The decoder โ the backend this time โ reports the max dynamic table size it accepts, and the negotiated dynamic table size is the minimum of this option's value and the value the backend specified. โจ
Default: 4K
--backend-http2-decoder-dynamic-table-size=<SIZE>โ sets the max dynamic table size of the HPACK decoder on the backend HTTP/2 connection. ๐ฆ
Default: 4K
๐ Mode ๐
- (default mode) โ this is the no-flag-needed vibe. It accepts HTTP/2 and HTTP/1.1 over SSL/TLS. If you pass the
"no-tls"parameter to the--frontendoption, it accepts HTTP/2 and HTTP/1.1 over cleartext TCP instead. And the incoming HTTP/1.1 connection can be upgraded to HTTP/2 through HTTP Upgrade. glow up in real time, fr fr โจ -s,--http2-proxyโ same energy as default mode, but with forward proxy enabled. This is the so-called HTTP/2 proxy mode. ๐
๐ Logging ๐
-L,--log-level=<LEVEL>โ sets the severity level of log output.<LEVEL>has to be one ofINFO,NOTICE,WARN,ERROR, orFATAL. No improvising, bestie, it's a closed set. ๐
Default: NOTICE
--accesslog-file=<PATH>โ sets the path to write the access log to. Wanna reopen the file? Send aUSR1signal tonghttpx. ๐--accesslog-syslogโ sends the access log to syslog. If you use this, the--accesslog-fileoption is ignored. One or the other, babe. ๐ซถ--accesslog-format=<FORMAT>โ sets the format string for the access log. The default format is combined format. Here are all the variables you can use, and yes, we're listing every single one because they all slay:$remote_addr: client IP address.$time_local: local time in Common Log format.$time_iso8601: local time in ISO 8601 format.$request: HTTP request line.$status: HTTP response status code.$body_bytes_sent: the number of bytes sent to client as response body.$http_<VAR>: value of HTTP request header<VAR>where'_'in<VAR>is replaced with'-'.$remote_port: client port.$server_port: server port.$request_time: request processing time in seconds with milliseconds resolution.$pid: PID of the running process.$alpn: ALPN identifier of the protocol which generates the response. For HTTP/1, ALPN is alwayshttp/1.1, regardless of minor version.$tls_cipher: cipher used for SSL/TLS connection.$tls_client_fingerprint_sha256: SHA-256 fingerprint of client certificate.$tls_client_fingerprint_sha1: SHA-1 fingerprint of client certificate.$tls_client_subject_name: subject name in client certificate.$tls_client_issuer_name: issuer name in client certificate.$tls_client_serial: serial number in client certificate.$tls_protocol: protocol for SSL/TLS connection.$tls_session_id: session ID for SSL/TLS connection.$tls_session_reused:"r"if SSL/TLS session was reused. Otherwise,"."$tls_sni: SNI server name for SSL/TLS connection.$tls_ech_accepted:"e"if ECH was accepted in SSL/TLS session. Otherwise,"."$backend_host: backend host used to fulfill the request."-"if backend host is not available.$backend_port: backend port used to fulfill the request."-"if backend host is not available.$method: HTTP method$path: Request path including query. For CONNECT request, authority is recorded.$path_without_query:$pathup to the first'?'character. For CONNECT request, authority is recorded.$protocol_version: HTTP version (e.g.,HTTP/1.1,HTTP/2)
And a little pro tip for disambiguation: you can wrap any variable in "{" and "}", like ${remote_addr}. iykyk ๐
Default: $remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
--accesslog-write-earlyโ writes the access log when the response header fields come in from the backend, instead of waiting for the whole request transaction to finish. Early bird energy. ๐ฆ--errorlog-file=<PATH>โ sets the path to write the error log to. To reopen the file, send aUSR1signal tonghttpx. Also,stderrgets redirected to the error log file unless--errorlog-syslogis used. ๐
Default: /dev/stderr
--errorlog-syslogโ sends the error log to syslog. If you use this, the--errorlog-fileoption is ignored. ๐ญ--syslog-facility=<FACILITY>โ sets the syslog facility to<FACILITY>.
Default: daemon
๐ธ HTTP ๐ธ
--add-x-forwarded-forโ appends theX-Forwarded-Forheader field to the downstream request. ๐--strip-incoming-x-forwarded-forโ strips theX-Forwarded-Forheader field from inbound client requests. bye bye โ๏ธ--no-add-x-forwarded-protoโ don't append the additionalX-Forwarded-Protoheader field to the backend request. And if the inbound client setsX-Forwarded-Proto, and the--no-strip-incoming-x-forwarded-protooption is used, those get passed through to the backend. ๐ซถ--no-strip-incoming-x-forwarded-protoโ don't strip theX-Forwarded-Protoheader field from inbound client requests. Let it live its truth. โจ--add-forwarded=<LIST>โ appends an RFC 7239Forwardedheader field with the parameters you give in the comma delimited list<LIST>. The supported parameters are"by","for","host", and"proto". By default, the values of the"by"and"for"parameters are obfuscated strings โ check--forwarded-byand--forwarded-forrespectively for that. And important note:nghttpxdoes not translate non-standardX-Forwarded-*header fields into theForwardedheader field, and vice versa. No crossover episodes, bestie. ๐--strip-incoming-forwardedโ strips theForwardedheader field from inbound client requests. ๐งน--forwarded-by=(obfuscated|ip|<VALUE>)โ sets the parameter value sent out with the"by"parameter of theForwardedheader field. If"obfuscated"is given, the string is randomly generated at startup. If"ip"is given, the interface address of the connection โ port number included โ is sent with the"by"parameter. In the case of a UNIX domain socket,"localhost"is used instead of the address and port. You can also specify a static obfuscated string, but the limitation is that it must start with"_", and it can only consist of the character set[A-Za-z0-9._-], as described in RFC 7239. Rules are rules. ๐
Default: obfuscated
--forwarded-for=(obfuscated|ip)โ sets the parameter value sent out with the"for"parameter of theForwardedheader field. If"obfuscated"is given, the string is randomly generated for each client connection. If"ip"is given, the remote client address of the connection โ port number not included this time โ is sent with the"for"parameter. In the case of a UNIX domain socket,"localhost"is used instead of the address. ๐
Default: obfuscated
--no-viaโ don't append to theViaheader field. And if aViaheader field is received, it's left unaltered. Touch nothing, break nothing. ๐--no-strip-incoming-early-dataโ don't strip theEarly-Dataheader field from inbound client requests. โญ--no-location-rewriteโ don't rewrite thelocationheader field in default mode. When--http2-proxyis used, thelocationheader field won't be altered regardless of this option โ it's just not that girl in proxy mode. ๐ฆ--host-rewriteโ rewrites thehostand:authorityheader fields in default mode. When--http2-proxyis used, these headers won't be altered regardless of this option. ๐--altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>โ specifies the protocol ID, port, host and origin of an alternative service.<HOST>,<ORIGIN>and<PARAMS>are optional. Empty<HOST>and<ORIGIN>are allowed, and they're treated as if nothing was specified. They get advertised in thealt-svcheader field only in the HTTP/1.1 frontend. You can use this option multiple times to specify multiple alternative services. Example:--altsvc="h2,443,,,ma=3600; persist=1"โจ--http2-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>โ just like the--altsvcoption, but this altsvc is only sent in the HTTP/2 frontend. Same slay, different protocol. ๐ฅ--add-request-header=<HEADER>โ bestie, this adds one more header field to the request header set. The field name must be lowercase (no shouting, we're not that kind of girl). This only appends โ it will NOT overwrite anything already sitting there. Use it as many times as you want to stack multiple header fields. Example:--add-request-header="foo: bar"--add-response-header=<HEADER>โ same energy, but for the response header set. Field name must be lowercase. It appends only and won't replace anything already set. Repeat it as many times as you need for multiple header fields. Example:--add-response-header="foo: bar"--request-header-field-buffer=<SIZE>โ the max buffer size for the incoming HTTP request header field list. Heads up: this counts the header name plus the value in bytes. And if trailer fields exist? They count towards this number too. ๐
Default: 64K
--max-request-header-fields=<N>โ caps how many incoming HTTP request header fields you'll accept. Trailer fields, if they exist, are counted here too.
Default: 100
--response-header-field-buffer=<SIZE>โ the max buffer size for the incoming HTTP response header field list. Sum of header name and value in bytes, and yes, trailer fields count towards it as well.
Default: 64K
--max-response-header-fields=<N>โ the maximum number of incoming HTTP response header fields. Trailer fields existing? Counted here too, no exceptions.
Default: 500
--error-page=(<CODE>|*)=<PATH>โ sets the file path to a custom error page that gets served when nghttpx originally generates an HTTP error status code of<CODE>. That<CODE>has to be greater than or equal to 400, and at most 599. Slap in*instead of<CODE>and it matches all HTTP status codes. ๐ One big caveat tho: if the error status code is coming from a backend server, these custom error pages are not used. Periodt.--server-name=<NAME>โ changes the server response header field value to<NAME>.
Default: nghttpx
--no-server-rewriteโ tells it to NOT rewrite the server header field in default mode. And when--http2-proxyis in play, those headers won't be altered regardless of this option, so don't even try it. ๐--redirect-https-port=<PORT>โ picks the port number that shows up in theLocationheader field when a redirect to an HTTPS URI happens because of the"redirect-if-not-tls"parameter in the--backendoption.
Default: 443
--require-http-schemeโ always requireshttporhttpsscheme in the HTTP request. It also requires thathttpsscheme must be used for an encrypted connection, and otherwisehttpscheme must be used. This option is recommended for a server deployment that directly faces clients and whose services only require thehttporhttpsscheme. ๐
โจ API โจ
--api-max-request-body=<SIZE>โ sets the maximum size of the request body for API requests.
Default: 32M
๐ DNS ๐
--dns-cache-timeout=<DURATION>โ how long cached DNS results stay valid. Lowkey important: nghttpx caches the unsuccessful results too. ๐ญ
Default: 10s
--dns-lookup-timeout=<DURATION>โ the timeout the DNS server gets to respond to the initial DNS query. For the 2nd and later queries, the server gets time based on this timeout, and it's scaled linearly. It's giving escalator energy.
Default: 250ms
--dns-max-try=<N>โ how many DNS queries happen before nghttpx gives up on the name lookup. ๐ซถ
Default: 3
--frontend-max-requests=<N>โ the number of requests that a single frontend connection can process. For HTTP/2, this is the number of streams in one HTTP/2 connection. For HTTP/1, it's the number of keep alive requests. Heads up, this is just a hint to nghttpx, so it may allow a few extra requests. ๐ธ
The default value is unlimited.
๐ Debug ๐
--frontend-http2-dump-request-header=<PATH>โ dumps request headers received by the HTTP/2 frontend to the file at<PATH>. The output is in HTTP/1 header field format and each header block is followed by an empty line. โ ๏ธ This option is not thread safe and MUST NOT be used with option-n<N>, where<N>>= 2.--frontend-http2-dump-response-header=<PATH>โ dumps response headers sent from the HTTP/2 frontend to the file at<PATH>. Output's in HTTP/1 header field format, each header block followed by an empty line. โ ๏ธ Also not thread safe and MUST NOT be used with option-n<N>, where<N>>= 2.-o,--frontend-frame-debugโ prints HTTP/2 frames in the frontend tostderr. โ ๏ธ Not thread safe and MUST NOT be used with option-n=N, where N >= 2.
๐ Process ๐
-D,--daemonโ runs in the background. And if you use-D, the current working directory gets changed to/. Iconic detached queen behaviour. ๐--pid-file=<PATH>โ sets the path to save the PID of this program.--user=<USER>โ runs this program as<USER>. This option is intended to be used to drop root privileges.--single-processโ runs this program in a single process mode for debugging purposes. Without this option, nghttpx creates at least 2 processes: main and worker processes. With this option, main and worker are unified into a single process. nghttpx still spawns an additional process if neverbleed is used. Also just so you know: in single process mode, the signal handling feature is disabled. ๐--max-worker-processes=<N>โ the maximum number of worker processes. nghttpx spawns a new worker process when it reloads its configuration. The previous worker process enters a graceful termination period and will terminate when it finishes handling the existing connections. BUT โ if reloading configurations happens very frequently, the worker processes might get piled up if they take a bit long to finish the existing connections. With this option, if the number of worker processes exceeds the given value, the oldest worker process is terminated immediately. Specifying0means no limit, and that's the default behaviour. ๐ซถ--worker-process-grace-shutdown-period=<DURATION>โ the maximum period for a worker process to terminate gracefully. When a worker process enters a graceful shutdown period (e.g., when nghttpx reloads its configuration) and it doesn't finish handling the existing connections in the given period of time, it gets terminated immediately. Specifying0means no limit, and that's the default behaviour.
๐ง Scripting ๐ง
--mruby-file=<PATH>โ sets the mruby script file.--ignore-per-pattern-mruby-errorโ ignores mruby compile errors for a per-pattern mruby script file. If an error occurred, it's treated as if no mruby file were specified for the pattern. No drama, we just move on. โจ
๐ฅ HTTP/3 and QUIC ๐ฅ
--frontend-quic-idle-timeout=<DURATION>โ specifies an idle timeout for a QUIC connection.
Default: 30s
--frontend-quic-debug-logโ outputs QUIC debug log to/dev/stderr. ๐--quic-bpf-program-file=<PATH>โ specifies a path to an eBPF program filereuseport_kern.oto direct an incoming QUIC UDP datagram to the correct socket.
Default: /usr/local/lib/nghttp2/reuseport_kern.o
--frontend-quic-early-dataโ enables early data on frontend QUIC connections. nghttpx sends the"Early-Data"header field to a backend server if a request is received in early data and the handshake has not finished. โ ๏ธ All backend servers should deal with possibly replayed requests.--frontend-quic-qlog-dir=<DIR>โ specifies a directory where a qlog file is written for frontend QUIC connections. A qlog file is created per each QUIC connection. The file name is ISO8601 basic format, followed by"-", the server Source Connection ID, and".sqlog". ๐--frontend-quic-require-tokenโ requires an address validation token for a frontend QUIC connection. The server sends a token in a Retry packet or aNEW_TOKENframe in the previous connection.--frontend-quic-congestion-controller=<CC>โ specifies a congestion controller algorithm for a frontend QUIC connection.<CC>should be either"cubic"or"bbr".
Default: cubic
--frontend-quic-secret-file=<PATH>โ path to a file that contains secure random data used as QUIC keying materials. It's used to derive keys for encrypting tokens and Connection IDs. It is not used to encrypt QUIC packets. Each line of this file must contain exactly 136 bytes of hex-encoded string (which, when decoded, is a byte string 68 bytes long). The first 3 bits of the decoded byte string are used to identify the keying material. An empty line, or a line that starts'#', is ignored. The file can contain more than one keying material. Because the identifier is 3 bits, at most 8 keying materials are read and the remaining data is discarded. The first keying material in the file is primarily used for encryption and decryption for a new connection. The other ones are used to decrypt data for the existing connections. Specifying multiple keying materials enables key rotation. ๐ Please note that key rotation does not occur automatically. The user should update files or change option values and restart nghttpx gracefully. If opening or reading the given file fails, all loaded keying materials are discarded and it's treated as if none of this option was given. If this option is not given, or an error occurred while opening or reading a file, a keying material is generated internally on startup and reload.--quic-server-id=<HEXSTRING>โ specifies the server ID encoded in the Connection ID to identify this particular server instance. The Connection ID is encrypted and this part is not visible in public. It must be 4 bytes long and must be encoded in a hex string (which is 8 bytes long). If this option is omitted, a random server ID is generated on startup and configuration reload. ๐ฆ--frontend-quic-initial-rtt=<DURATION>โ sets the initial RTT of the frontend QUIC connection, bestie โจ
Default: 333ms
--no-quic-bpfโ turns eBPF off. That's it, that's the whole job ๐--frontend-http3-window-size=<SIZE>โ sets the per-stream initial window size for the HTTP/3 frontend connection ๐ฆ
Default: 256K
--frontend-http3-connection-window-size=<SIZE>โ sets the per-connection window size for the HTTP/3 frontend connection ๐ธ
Default: 1M
--frontend-http3-max-window-size=<SIZE>โ sets the maximum per-stream window size for the HTTP/3 frontend connection. The window size gets adjusted based on the receiving rate of stream data. The initial value is whatever you gave to--frontend-http3-window-size, and the window grows up to<SIZE>bytes ๐
Default: 6M
--frontend-http3-max-connection-window-size=<SIZE>โ sets the maximum per-connection window size for the HTTP/3 frontend connection. Same deal: the window size adjusts based on the receiving rate of stream data, it starts at the value from--frontend-http3-connection-window-size, and it grows up to<SIZE>bytes ๐
Default: 8M
--frontend-http3-max-concurrent-streams=<N>โ sets the max number of concurrent streams allowed in one frontend HTTP/3 connection ๐
Default: 100
๐ Misc ๐
--conf=<PATH>โ load the configuration from<PATH>. Quick heads up though: if you don't give--conf, nghttpx always tries to read the default configuration file anyway ๐
Default: /etc/nghttpx/nghttpx.conf
--include=<PATH>โ load extra configs from<PATH>. The file<PATH>gets read right when the configuration parser stumbles onto this option. You can use this one multiple times, or even recursively, no cap ๐-v,--versionโ print the version and dip. bye bestie ๐-h,--helpโ print this help and exit ๐
The <SIZE> argument is an integer plus an optional unit (so like, 10K means 10 * 1024). The units are K, M and G (all powers of 1024, slay).
The <DURATION> argument is an integer plus an optional unit (so 1s is 1 second, 500ms is 500 milliseconds). The units are h, m, s or ms (hours, minutes, seconds and milliseconds, respectively). If you skip the unit, a second is used as the unit โฑ๏ธ
๐ FILES ๐
/etc/nghttpx/nghttpx.confโ the default config file path nghttpx searches at startup. You can change the config file path with the--confoption.
Any lines starting with # are treated as comments ๐ฌ
The option name in the config file is the long command-line option name with the leading -- stripped off (so like, frontend). Put = between the option name and the value. And do NOT add extra leading or trailing spaces, okay? ๐ซถ
When you're specifying arguments that contain characters a shell finds special, we usually use quotes so the shell doesn't interpret them. When you're writing this config file though, those quotes must not be used. For example, to specify an additional request header field, you do this:
add-request-header=foo: bar
instead of:
add-request-header="foo: bar"
The options that don't take an argument on the command line do take an argument in the config file. Specify yes as the argument (e.g., http2-proxy=yes). If you give any other string, it just gets ignored ๐
To specify the private key and certificate file โ the ones given as positional arguments on the command line โ use private-key-file and certificate-file.
The --conf option cannot be used in the config file, and it will be ignored if you specify it there. periodt ๐ซ
Error log
The error log is written to stderr by default. You can configure it with --errorlog-file. The format of a log message is like this:
<datetime> <main-pid> <current-pid> <thread-id> <level> (<filename>:<line>) <msg>
<datetime>โ a combination of the date and time when the log was written. It's in ISO 8601 format ๐<main-pid>โ the main process ID.<current-pid>โ the process ID that wrote this log.<thread-id>โ the thread ID that wrote this log. It would be unique within<current-pid>.<filename>and<line>โ the source file name and line number that produced this log.<msg>โ the log message body.
๐จ SIGNALS ๐จ
SIGQUITโ shut down gracefully. First it accepts pending connections and stops accepting new ones. Once all connections are handled, nghttpx exits ๐SIGHUPโ reload the configuration file given in--conf๐SIGUSR1โ reopen the log files ๐SIGUSR2โ fork and execute nghttpx. It'll execute the binary at the same path with the same command-line arguments and environment variables. As of nghttpx version1.20.0, the new main process sendsSIGQUITto the original main process when it's ready to serve requests. For earlier versions of nghttpx, you have to sendSIGQUITto the original main process yourself ๐ญ
The difference between SIGUSR2 (+ SIGQUIT) and SIGHUP is that the former is usually used to execute a new binary, and the main process is newly spawned. The latter, on the other hand, just reloads the configuration file, and the same main process keeps existing โจ
Note: nghttpx consists of multiple processes: one process for processing these signals, and another one for processing requests. The former spawns the latter. The former is called the main process, and the latter is called the worker process. If neverbleed is enabled, the worker process spawns a neverbleed daemon process that does the RSA key processing. The above signals must be sent to the main process. If any of the other processes receive one of them, it's ignored. This behaviour of these processes may change in a future release. In other words, in a future release, processes other than the main process may terminate upon receiving these signals. So these signals should not be sent to processes other than the main process ๐
๐ SERVER PUSH ๐
nghttpx supports HTTP/2 server push in default mode, via the Link header field. nghttpx looks for the Link header field (RFC 5988 <http://tools.ietf.org/html/rfc5988>) in response headers from the backend server, extracts the URI-reference with the parameter rel=preload (see preload <http://w3c.github.io/preload/#interoperability-with-http-link-header>), and pushes those URIs to the frontend client. Here's a sample Link header field to get a server push going:
Link: </fonts/font.woff>; rel=preload
Link: </css/theme.css>; rel=preload
Right now, the following restriction applies to server push:
- The associated stream must have method
"GET"or"POST". The associated stream's status code must be200.
This limitation may be loosened in a future release ๐ค
nghttpx also supports server push if both frontend and backend are HTTP/2 in default mode. In that case, on top of server push via the Link header field, server push from the backend gets forwarded to the frontend HTTP/2 session ๐ฅ
HTTP/2 server push gets disabled if --http2-proxy is used ๐ซ
๐ UNIX DOMAIN SOCKET ๐
nghttpx supports UNIX domain socket with a filename for both frontend and backend connections, iconic ๐
Please note that the current nghttpx implementation does not delete a socket with a filename. And on startup, if nghttpx detects that the specified socket already exists in the filesystem, nghttpx first deletes it. However, if SIGUSR2 is used to execute a new binary and both the old and new configurations use the same filename, the new binary does not delete the socket and continues to use it. iykyk ๐ซถ
๐ TLS SESSION RESUMPTION ๐
nghttpx supports TLS session resumption through both session ID and session ticket โจ
SESSION ID RESUMPTION
By default, the session ID is shared by all worker threads ๐งต
TLS SESSION TICKET RESUMPTION
By default, the session ticket is shared by all worker threads. Automatic key rotation is also enabled by default. Every hour, a new encryption key is generated, and the previous encryption key becomes a decryption-only key. We set the session timeout to 12 hours, so we keep at most 12 keys ๐
If --tls-ticket-key-memcached is given, encryption keys are retrieved from memcached. nghttpx just reads keys from memcached; you have to deploy a key generator program to update the keys frequently (like, every 1 hour). The example key generator tlsticketupdate.go is available under the contrib directory in the nghttp2 archive. The memcached entry key is nghttpx:tls-ticket-key. The data format stored in memcached is the binary format described below:
+--------------+-------+----------------+
| VERSION (4) |LEN (2)|KEY(48 or 80) ...
+--------------+-------+----------------+
^ |
| |
+------------------------+
(LEN, KEY) pair can be repeated
All numbers in the above figure are in bytes. All integer fields are in network byte order ๐
The first 4-byte integer is the VERSION field, which must be 1. The 2-byte integer LEN field gives the length of the following KEY field, which contains the key. If --tls-ticket-key-cipher=aes-128-cbc is used, LEN must be 48. If --tls-ticket-key-cipher=aes-256-cbc is used, LEN must be 80. The LEN and KEY pair can be repeated multiple times to store multiple keys. The key that appears first is used as the encryption key. All the remaining keys are used as decryption only ๐
By default, connections to the memcached server are not encrypted. To enable encryption, use the tls keyword in the --tls-ticket-key-memcached option ๐ก๏ธ
If --tls-ticket-key-file is given, the encryption key is read from the given file. In this case, nghttpx does not rotate the key automatically. To rotate the key, you have to restart nghttpx (see SIGNALS) ๐ซ
okay so nghttpx is literally obsessed with the TLS signed_certificate_timestamp extension (that's RFC 6962 <https://tools.ietf.org/html/rfc6962>) ๐
the โจrelevantโจ options here are --tls-sct-dir and the sct-dir parameter that lives inside --subcert. both of them take a directory, and nghttpx will read every single file whose extension is .sct under that directory. periodt.
those *.sct files are encoded as the SignedCertificateTimestamp struct described in section 3.2 of RFC 69662 <https://tools.ietf.org/html/rfc6962#section-3.2>. and bestie, this is exactly the same format that nginx-ct <https://github.com/grahamedgecombe/nginx-ct> and mod_ssl_ct <https://httpd.apache.org/docs/trunk/mod/mod_ssl_ct.html> use ๐ซถ
and if you wanna actually submit certificates to log servers and pull back the SignedCertificateTimestamp struct that nghttpx can eat? that's literally what ct-submit <https://github.com/grahamedgecombe/ct-submit> is for ๐
๐ฅ MRUBY SCRIPTING ๐ฅ
โ ๏ธ Warning: the mruby extension API right now is experimental and not frozen. it is subject to change in a future release. so like, don't come for me ๐
โ ๏ธ Warning: okay this one matters โ almost every string value you get back from a method or an attribute is a fresh new mruby string, which means memory allocation and copies, every time ๐ญ so it's strongly recommended that you stash that return value in a local variable and use that, instead of calling the method or touching the attribute over and over. iykyk.
nghttpx lets you level up its whole capability set with mruby scripts ๐ there are 2 hook points where your mruby script gets to run: the request phase and the response phase.
- the request phase hook fires after all request header fields have been received from the client.
- the response phase hook fires after all response header fields have been received from the backend server.
these hooks let you modify header fields, mess with common HTTP variables like authority or request path, and even return a custom response without ever forwarding the request to the backend servers ๐
there are 2 levels of mruby script invocation: global and per-pattern.
- the global mruby script is set by the
--mruby-fileoption, and it's called for all requests. - the per-pattern mruby script is set by the
"mruby"parameter in the-boption, and it's invoked for a request that matches that particular pattern.
the order of hook invocation goes like this, no cap:
- global request phase hook
- per-pattern request phase hook
- per-pattern response phase hook
- finally, global response phase hook
if a hook returns a response, any later hooks are not invoked. and heads up: the global request hook runs before pattern matching happens, so changing the request path can totally affect the pattern matching ๐ฆ
one more thing โ please note that the request and response hooks of a per-pattern mruby script for a single request might not come from the same script. this can happen when a request hook is executed, the backend fails for some reason, and at the same time the backend configuration gets replaced by an API request, and then the request uses the new configuration on retry. in that case, the response hook from the new configuration, if one is specified, will be invoked. it's giving chaos but okay โจ
all mruby scripts get evaluated once per thread on startup, and each one must instantiate an object and evaluate it as the return value (like App.new). that object is called the app object ๐
- if the app object defines an
on_reqmethod, it gets called with anNghttpx::Envobject on the request hook. - if the app object defines an
on_respmethod, it gets called with anNghttpx::Envobject on the response hook.
for each method invocation, you can access the Nghttpx::Request and Nghttpx::Response objects via Nghttpx::Env#req and Nghttpx::Env#resp respectively ๐ธ
โญ Nghttpx::REQUEST_PHASE
constant that represents the request phase. iconic, self-explanatory.
โญ Nghttpx::RESPONSE_PHASE
constant that represents the response phase. also iconic.
๐ class Nghttpx::Env
object that represents the current request-specific context.
attributes
attribute [R] reqโ returns theRequestobject.attribute [R] respโ returns theResponseobject.attribute [R] ctxโ returns a Ruby hash object. it persists until the request finishes, so any values you set in the request phase hook can be retrieved in the response phase hook ๐attribute [R] phaseโ returns the current phase.attribute [R] remote_addrโ returns the IP address of the remote client. if the connection is made via a UNIX domain socket, this returns the string"localhost".attribute [R] server_addrโ returns the address of the server that accepted the connection. this is a string, the one you specified in--frontend, excluding the port number โ and not a resolved IP address. for a UNIX domain socket, this is the path to the UNIX domain socket.attribute [R] server_portโ returns the port number of the server frontend which accepted the connection from the client.attribute [R] tls_usedโ returnstrueif TLS is used on the connection.attribute [R] tls_sniโ returns the TLS SNI value that the client sent on this connection.attribute [R] tls_client_fingerprint_sha256โ returns the SHA-256 fingerprint of a client certificate.attribute [R] tls_client_fingerprint_sha1โ returns the SHA-1 fingerprint of a client certificate.attribute [R] tls_client_issuer_nameโ returns the issuer name of a client certificate.attribute [R] tls_client_subject_nameโ returns the subject name of a client certificate.attribute [R] tls_client_serialโ returns the serial number of a client certificate.attribute [R] tls_client_not_beforeโ returns the start date of a client certificate, in seconds since the epoch.attribute [R] tls_client_not_afterโ returns the end date of a client certificate, in seconds since the epoch.attribute [R] tls_cipherโ returns the TLS cipher negotiated on this connection.attribute [R] tls_protocolโ returns the TLS protocol version negotiated on this connection.attribute [R] tls_session_idโ returns the session ID for this connection, as a hex string.attribute [R] tls_session_reusedโ returnstrueif, and only if, an SSL/TLS session is reused.attribute [R] alpnโ returns the ALPN identifier negotiated on this connection.attribute [R] tls_handshake_finishedโ returnstrueif the SSL/TLS handshake has finished. if it returnsfalsein the request phase hook, the request was received in TLSv1.3 early data (0-RTT) and might be vulnerable to the replay attack ๐ nghttpx will send anEarly-Dataheader field to backend servers to flag this.
๐ class Nghttpx::Request
object that represents a request from the client. heads up: you're only allowed to modify the Request object in the request phase hook.
attributes
attribute [R] http_version_majorโ returns the HTTP major version.attribute [R] http_version_minorโ returns the HTTP minor version.attribute [R/W] methodโ the HTTP method. on assignment, a copy of the given value is assigned. we don't accept arbitrary method names โ those will get documented later, but well-known methods likeGET,PUTandPOSTare all supported ๐attribute [R/W] authorityโ the authority (i.e.,example.org), including the optional port component. on assignment, a copy of the given value is assigned.attribute [R/W] schemeโ the scheme (i.e.,http,https). on assignment, a copy of the given value is assigned.attribute [R/W] pathโ the request path, including the query component (i.e.,/index.html). on assignment, a copy of the given value is assigned. the path does not include the authority component of the URI, but it may include the query component. nghttpx does some normalization on the path: it decodes percent-encoding for unreserved characters (see <https://tools.ietf.org/html/rfc3986#section-2.3>), and it resolves".."and".". but it may leave characters that should be percent-encoded as-is. so be careful when comparing a path against a desired string ๐ซถattribute [R] headersโ returns a Ruby hash containing a copy of the request header fields. changing values in the returned hash does not change the request header fields actually used in request processing ๐ญ useNghttpx::Request#add_headerorNghttpx::Request#set_headerto actually change request header fields.
methods
add_header(key, value)โ add a header entry associated withkey.valuecan be a single string or an array of strings. it does not replace any existing values associated withkey.set_header(key, value)โ set the header entry associated withkey.valuecan be a single string or an array of strings. it replaces any existing values associated withkey.clear_headers()โ clear all existing request header fields. bye ๐push(uri)โ initiate a push of the resource identified byuri. only HTTP/2 supports this feature โ for the other protocols, this method is a noop.urican be an absolute URI, an absolute path, or a relative path to the current request. for absolute or relative paths, the scheme and authority are inherited from the current request. currently, the method is alwaysGET. nghttpx will issue a request to the backend servers to fulfill this request, and the request and response phase hooks will be called for the pushed resource as well โจ
๐ class Nghttpx::Response
object that represents the response from the backend server.
attributes
attribute [R] http_version_majorโ returns the HTTP major version.attribute [R] http_version_minorโ returns the HTTP minor version.attribute [R/W] statusโ the HTTP status code. it must be in the range[200, 999], inclusive. also, just so you know, the non-final status code is not supported in mruby scripting at the moment ๐attribute [R] headersโ returns a Ruby hash that is a copy of your response header fields. Lowkey important: mutating the values in that returned hash does not change the response header fields that actually get used in response processing, bestie. If you wanna actually change them useNghttpx::Response#add_headerorNghttpx::Response#set_header. Periodt. ๐add_header(key, value)โ adds a header entry under key. value</p> can be a single string or an array of strings. It does not replace any existing values already chilling under that key โ it just stacks another one on top. ๐set_header(key, value)โ sets a header entry under key. value</p> can be a single string or an array of strings. This one replaces any existing values associated with key. Bye bye old values ๐clear_headers()โ clears all existing response header fields. Total clean slate energy โจreturn(body)โ returns a custom response body to a client. Real talk, this one changes how stuff flows:- Call it in the request phase hook โ the request is not forwarded to the backend, and the response phase hook for this request will not be invoked.
- Call it in the response phase hook โ the response from the backend server is canceled and discarded.
Set your status code and response header fields before using this method. Status code? Use Nghttpx::Response#status. If you don't set a status code, 200 is used. Header fields? Use Nghttpx::Response#add_header and Nghttpx::Response#set_header. When you invoke this in the response phase hook, the response headers are filled with the ones received from the backend server โ so to send completely custom header fields, first call Nghttpx::Response#clear_headers to erase all existing header fields, then add what you need. ๐ง
Calling this method twice for a given request is an error. Once is the vibe, twice is not. ๐
send_info(status, headers)โ sends a non-final (informational) response to a client. status</p> must be in the range[100, 199], inclusive. headers</p> is a hash containing response header fields โ its key must be a string, and the associated value must be either a string or an array of strings. Since this is not a final response, even if this method is invoked, the request is still forwarded to a backend unlessNghttpx::Response#returnis called. This method can be called multiple times. It cannot be called afterNghttpx::Response#returnis called. ๐ฆ
๐ MRUBY EXAMPLES ๐
Modify request path:
class App
def on_req(env)
env.req.path = "/apps#{env.req.path}"
end
end
App.new
Don't forget to instantiate and evaluate the object at the last line! That App.new is not decorative, bestie, it's the whole point ๐
Restrict permission of viewing a content to a specific client addresses:
class App
def on_req(env)
allowed_clients = ["127.0.0.1", "::1"]
if env.req.path.start_with?("/log/") &&
!allowed_clients.include?(env.remote_addr) then
env.resp.status = 404
env.resp.return "permission denied"
end
end
end
App.new
โจ API ENDPOINTS โจ
nghttpx exposes API endpoints to manipulate it via an HTTP based API. By default, the API endpoint is disabled. To enable it, add a dedicated frontend for the API using the --frontend option with the "api" parameter. All requests which come from this frontend address will be treated as API requests. ๐
The response is normally a JSON dictionary, and at least includes the following keys:
statusโ the status of the request processing. The following values are defined:Successโ the request was successful. ๐Failureโ the request was failed. No change has been made. ๐
codeโ HTTP status code.
Additionally, depending on the API endpoint, a data key may be present, and its value contains the API endpoint specific data.
We wrote "normally", since nghttpx may return an ordinal HTML response in some cases where the error has occurred before reaching the API endpoint (e.g., header field is too large). ๐
The following section describes the available API endpoints.
POST /api/v1beta1/backendconfig
This API replaces the current backend server settings with the requested ones. The request method should be POST, but PUT is also acceptable. The request body must be nghttpx configuration file format โ for the configuration file format, see the FILES section. The line separator inside the request body must be a single LF (0x0A). Currently, only the backend option is parsed; the others are simply ignored.
The semantics of this API is: replace the current backend with the backend options in the request body. Describe the desired set of backend servers, and nghttpx makes it happen. โจ If there is no backend option found in the request body, the current set of backend is replaced with the backend option's default value, which is 127.0.0.1,80.
The replacement is done instantly without breaking existing connections or requests. It also avoids any process creation as is the case with hot swapping with signals. Smoooooth.
The one limitation is that only a numeric IP address is allowed in backend in the request body unless the "dns" parameter is used, while a non numeric hostname is allowed in the command-line or configuration file read using --conf. ๐
GET /api/v1beta1/configrevision
This API returns the configuration revision of the current nghttpx. The configuration revision is an opaque string, and it changes after each reloading by SIGHUP. With this API, an external application knows whether nghttpx has finished reloading its configuration by comparing the configuration revisions between before and after reloading. It is recommended to disable the persistent (keep-alive) connection for this purpose, in order to avoid sending a request using the reused connection which may be bound to an old process. ๐
This API returns a response including the data key. Its value is a JSON object, and it contains at least the following key:
configRevisionโ the configuration revision of the currentnghttpx.
๐ SEE ALSO ๐
nghttp(1), nghttpd(1), h2load(1)
โจ Author โจ
Tatsuhiro Tsujikawa ๐
โจ Copyright โจ
2012, 2015, 2016, Tatsuhiro Tsujikawa
That's a wrap, bestie ๐ you just read the whole thing, from source version 1.70.0, dated Jul 29, 2026 โ the very last line of the page, and honestly? Iconic. Go forth and proxy your little heart out ๐โจ๐ฆ๐ธ๐
๐ฆ where this page comes from
- alpine 3.24: nghttp2-doc 1.70.0-r0 ยท MIT
- nixpkgs unstable: nghttp2 1.70.0
- ubuntu stonking: nghttp2-proxy 1.70.0-1 ยท MIT
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms