POUND(8)
๐
POUND(8) โ pound (System Manager's Manual), bestie edition
๐ NAME ๐
pound โ HTTP/HTTPS reverse-proxy and load-balancer. Thatโs the whole vibe: it catches requests up front and shares them out to your backends. ๐
โจ SYNOPSIS โจ
Okay so hereโs the command-line fit check, exactly as the page spells it:
pound [-FVcehv] [-W [no-]FEATURE] [-f CONF-FILE] [-p PID-FILE]
Yep, thatโs the whole synopsis. No cap. ๐ซถ
๐ธ DESCRIPTION ๐ธ
Bestie, pound is a reverse-proxy load balancing server. It accepts requests from HTTP/HTTPS clients and distributes them to one or more Web servers (backends). Those requests may be passed to backends as plain HTTP or re-encrypted as HTTPS. Iconic. โจ
If more than one backend server is defined, pound chooses one of them randomly, based on the requested balancing algorithm and backend priorities. By default, pound keeps track of associations between clients and backend servers (sessions). ๐
๐ GENERAL PRINCIPLES ๐
In general pound needs three types of objects defined in order to function: listeners, services and backends. Think of it as the holy trinity of the config, fr fr. ๐ง
๐ง Listeners
A listener is a definition of how pound receives requests from the clients (browsers). Listeners of two types are supported: regular HTTP listeners and HTTPS listeners. In the simplest case, a listener must define the address and port to listen on, with additional requirements for HTTPS listeners. Lowkey straightforward, highkey important. ๐
๐ฆ Services
A service routes requests to backends. Services may be defined within a listener or in global scope. When a request is received, pound attempts to match it to each service in turn, starting with the services defined in the listener itself and, if needed, continuing with those defined at the global level. Services may define their own conditions as to which requests they can answer: typically this involves certain URLs (e.g. images only, or a certain path) or specific headers (such as the Host header). A service may also define a session mechanism: if so, future requests from a given client will always be answered by the same backend. Main character energy for real. ๐
๐งธ Backends
Backends define where to obtain the content requested. Most often, they refer to โrealโ web servers and define how to contact them. ๐
Several types of backends are defined:
๐งท Regular backend
Regular backends are the most often used type of backends and the raison dโetre of pound. A regular backend passes received requests to a backend server, waits for it to respond and sends the response back to the requesting client. Reliable king behavior. ๐
๐จ Emergency backend
A special case of regular backend which will be used only if all other regular backends fail to respond. Break-glass bestie. ๐ฅ
โช๏ธ Redirect backend
A redirect backend responds to each request with a redirect response. See Redirect, below. ๐ซถ
๐งพ ACME backend
A special backend designed to handle ACME challenges. Backends of this type are used to handle HTTP-01 authorization when re-issuing LetsEncrypt certificates.
See the ACME section below. ๐ฑ
๐ Error backend
The error backend creates and returns a predefined HTTP status response. Itโs giving โnope, and hereโs the receipt.โ ๐ญ
๐ Static file backend
This backend responds with a contents of a disk file. Simple, clean, slay. ๐
Multiple regular or emergency backends may be defined within a service, in which case pound will balance the load between the available backends. โ๏ธ
Two types of load-balancing strategies are implemented: random balancing (default), and interleaved weighted round-robin balancing. Choices, choices. โจ
If a regular backend fails to respond, it will be considered โdeadโ, in which case pound will stop sending requests to it. Dead backends are periodically checked for availability, and once they respond again they are โresurrectedโ and requests are sent again their way. If no backends are available (none were defined, or all are โdeadโ), then pound will reply with 503 Service Unavailable, without checking additional services. Dramatic? Maybe. Accurate? Absolutely. ๐
Normally, pound communicates with its backends via plain HTTP. It is, however, possible to use HTTPS as well. ๐
A working pound configuration may define multiple listeners and services. They can be identified either by 0-based ordinal number within the configuration (or, for services, within the listener) or by their symbolic name. Backends are identified by their ordinal number within the service. Numbers and names, bestie โ know the difference. ๐
๐ก REQUEST BALANCING ๐ก
Load balancing strategy defines algorithm used to distribute incoming requests between multiple regular backends. Each backend is assigned a priority โ a positive number indicating its relative weight among other backends. The share of requests a backend handles can be estimated as:
Pi / S(P)
where Pi is priority of the backend with index i, and S(P) is sum of all priorities. Math but make it fashion. ๐งฎ
Two balancing strategies are implemented:
๐ฒ Weighted Random Balancing
This is the default strategy. The backend to use for each request is determined at random taking into account backend priorities, so that backends with numerically greater priorities have proportionally greater chances of being selected than the ones with lesser priorities. RNG with a weighted little twist. ๐
๐ Interleaved Weighted Round Robin Balancing
This strategy cycles over all active backends, considering each one in its turn. An integer ordinal number is assigned to each round, which is incremented (modulo number of backends) each time a new round is started. A backend is assigned a request only if its priority is greater than the round number. Round and round and round we go. ๐
This strategy offers several advantages compared with the previous one. First, it results in a more even distribution of the requests. Secondly, the resulting distribution is predictable. Predictable is such a flex. โจ
Within each Service, multiple backends are grouped in balancer groups. Each such group is assigned a unique integer priority, which defines the order in which the groups are tried. When selecting a backend, a group with the numerically lesser priority is selected and a suitable backend is looked up among backends defined in the group, using one of the balancing strategies discussed above. If no backend can be selected (e.g. all backends are unavailable), next group is tried. The process continues until either a backend is chosen or the list of balancer groups is exhausted. Group chat hierarchy, but make it infrastructure. ๐
By default, backends declared using the Backend keyword are assigned to the balancer group 0. These are backends used during normal operation. Backends declared using the Emergency keyword are assigned to the balancer group 65535. These backends form a pull of high availability backends, which will be tried only if all of the normal backends fail. Break-glass group has entered the chat. ๐จ
More backend groups can be added using dynamic backends, discussed below. ๐
๐งต WORKER MODEL ๐งต
Each incoming connection is assigned to a specific worker, i.e. a thread in the running program. This worker processes all requests that arrive via that connection. One connection, one worker, no sharing drama. ๐ซถ
The number of running workers is controlled by three configuration parameters. WorkerMinCount defines the minimum number of workers that should always be running (5, by default). Another parameter, WorkerMaxCount sets the upper limit on the number of running workers (it defaults to 128). Minimum vibe, maximum vibe. ๐
At each given moment, a worker can be in one of two states: idle or active (handling a connection). If an incoming connection arrives when all running workers are active, and total number of workers is less than WorkerMaxCount, a new worker is started and the new connection is handed to it. If the number of active workers has already reached maximum, the new connection is added to the connection queue, where it will wait for a worker to become available to process it. If the queue becomes full as well, the program stops accepting new connections until at least one worker becomes available again. Queue full = no new friends right now. ๐ญ
The third parameter, WorkerIdleTimeout, specifies maximum time a thread is allowed to spend in the idle state. If a worker remains idle longer than that and total number of workers is greater than the allotted minimum (WorkerMinCount), the idle worker is terminated. Idle time is over, bestie. ๐
The capacity of the connection queue is computed so as to accept as many connection requests as is permitted by the current system limit on the open files. It can also be set using the ConnectionQueueSize configuration directive. Queue math: handled. โ
โจ OPTIONS โจ
okay bestie, here's the command line menu ๐
these are all the flags you can throw at pound:
-cโ check only! pound exits immediately after parsing the configuration file. literally perfect for running a quick syntax check before you actually activate a server ๐ซถ-eโ log to standard error (standard output forLOG_DEBUGandLOG_INFOseverity levels). both standard streams get set to line buffering mode. this option implies foreground mode (-F) and overrides theLogLevelconfiguration setting. lowkey a two-for-one ๐ญ-Fโ foreground mode. the program will not detach from the controlling terminal and will remain in foreground after startup. this overrides theDaemonconfiguration setting. the log stream (syslog facility or stderr) requested in the configuration remains in effect. see also the-eoption, above ๐-f FILEโ location of the configuration file (see below for a full description of the format). default is$sysconfdir/pound.cfg, where$sysconfdirstands for the system configuration directory, as determined at build time. most often it is either/usr/local/etc, or/etc. slay ๐-hโ print short command line usage summary and exit. the "help me bestie" flag โจ-p pid_fileโ location of the PID file. pound will write its own PID into this file. normally this is used for shell scripts that control starting and stopping of the daemon. see the description of thePIDFilestatement in the GLOBAL DIRECTIVES section below, for a detailed discussion of this file โ-vโ verbose mode: during startup, error messages will be sent to stderr (stdout forLOG_DEBUGandLOG_INFOseverity levels). if pound was configured to log to syslog, error diagnostics will be duplicated there as well. after startup the configuration settings take effect. nosy but make it useful ๐-Vโ print version: pound will exit immediately after printing the current version, licensing terms, and configuration flags. like a little business card ๐-W [no-]FEATUREโ enable or disable (if prefixed withno-) additional pound features. as of this version, the following FEATUREs are implemented:[no-]warn-deprecatedโ warn if any deprecated statements are used in configuration file. your fave little "girl what are you doing" alarm ๐[no-]dnsโ resolve host names found in configuration file. this is the default. you can disable it if your configuration file refers to backends only by their IP addresses, in order to suppress potentially lengthy network host address lookups. bye bye waiting around ๐ฆno-include-dirโ don't set the include directory to the system configuration directory. this means that each relative filename used in arguments to the directives in the configuration file will be looked up in the current working directory. this feature is useful mainly in testsuite. iykyk ๐งinclude-dir=DIRโ override the default include directory setting. set it toDIR. see the discussion of theIncludeDirdirective in section GLOBAL DIRECTIVES, below ๐ธ
๐ CONFIGURATION FILE ๐
each line in the file is considered a complete configuration directive. empty lines and comments are ignored โ as they should be ๐
. comments are introduced with a # sign and extend to the end of line on which it appears.
there are three types of directives, and yes they all have main character energy:
- global directives โ they affect the settings for the entire program instance ๐
- listener directives โ they define which requests pound will listen for ๐
- service directives โ they affect only a specific group of requests ๐ฏ
in general, a directive consists of a keyword and one or more values, separated by any amount of whitespace. leading and trailing whitespace is ignored. keywords are case-insensitive (so no stress about caps, bestie โจ). a value can be:
- Numeric โ a decimal number. ๐ข
- Boolean โ the words
yes,true,on, or1indicating true, andno,false,off, or0indicating false. all words are case-insensitive. no cap ๐ - String โ any sequence of characters between double-quotes. a backslash is treated as an escape character: if it is followed by a double-quote or another backslash, it is removed and the character after it is read literally. if it is followed by any other character, a warning message is printed. she's a little dramatic like that ๐ญ
- Identifier โ a sequence of characters starting with an ASCII letter and consisting of letters, digits and underscores ๐
- IP address โ an IPv4 or IPv6 address in numeric form, or a hostname ๐
unless specified otherwise, directives may appear in any order. freedom, honestly ๐ฆ
๐ GLOBAL DIRECTIVES ๐
global directives may appear anywhere at the top level within the configuration file, although it is customary for them to be at the start. aesthetic, y'know ๐
๐ซ Program settings ๐ซ
Daemon boolโ instructs pound to run in the foreground (if false) or as a daemon (if true). by default pound runs as a daemon (i.e. detaches itself from the controlling terminal and continues operating in the background). by setting this directive to false you can force pound to work like a regular process. see also-Fand-eoptions, above. choose your fighter โจBalancer random | iwrrโ defines the load-balancing strategy to use. possible arguments are:random, to use weighted random balancing algorithm, andiwrr, meaning interleaved weighted round robin balancing. see REQUEST BALANCING, above, for a detailed discussion of these balancing strategies ๐ฅ
the Balancer statement in the global scope applies to all Service directives that don't contain Balancer definitions of their own. so it's like the default outfit and the services can change if they want ๐
Constant "NAME" [OPTIONS] ["VALUE"]โ defines a configuration constant for use in string expansions and Lua code.NAMEgives the name of the constant. either OPTIONS or VALUE must be given, but not both. pick one, bestie ๐
if OPTIONS are not given, VALUE supplies the value for the constant. otherwise, the value is assigned as described in the table below:
-file "FILENAME"โ use contents of the file"FILENAME". the contents is treated as an opaque binary string. the file is read exactly once, at program startup. one and done ๐-filewatch "FILENAME"โ same as-file, except thatFILENAMEis monitored for changes during the runtime. whenever the file changes, it will be re-read, and the constant will be updated, without the need to restart the program. she stays updated fr fr ๐-trimโ used with one of the above, instructs pound to treatFILENAMEas a plain text file, and remove from its contents all trailing whitespace characters, i.e. horizontal tabulation (ASCII 9), newline (ASCII 10), vertical tabulation (ASCII 11), form feed (ASCII 12), carriage return (ASCII 13), and space (ASCII 32). the cleanup queen ๐งน
ConnectionQueueSize "N"โ set the capacity of the incoming connection queue. the default value is computed as a function ofWorkerMaxCountand the system limit on open file descriptors. see the description in WORKER MODEL, and that of theReserveFDstatement, below. math but make it slay ๐ขControl "pathname"โ set the control socket path. see the Control socket section below, for a detailed description of this feature ๐๏ธIgnoreCase boolโ ignore letter case when doing regex matching (default:false). this directive sets the default for the following service matching directives:URL,Path,QueryParam,Query,StringMatch, as well as for theDeleteHeadermodification directive. its value can be overridden for specific services ๐ค
โ ๏ธ this statement is deprecated and will be removed in future versions. please, use the -icase option to the service matching directive instead. see the discussion of options in Service Matching Directives section below. don't say i didn't warn you ๐
LineBufferSize Nโ sets the line buffer size. line buffer is used when reading request line, backend response status line, and header fields in both requests and responses. thus, this value limits the maximum length allowed for these parts of HTTP messages. by default, it is set to the internal buffer size (4096 bytes, configurable at compile time using the--with-maxbufoption). it cannot be set to a value less than that default. so 4096 is the floor, periodt ๐ซPIDFile "filename"โ sets the name of the file where to store program PID. it can be overridden by the-pcommand line option.
notice the following:
- when running with a supervisor, this file holds PID of the supervisor process. otherwise, it holds PID of the main pound process. this means it is always suitable for signalling the program using the traditional
kill-with-cattechnique:
kill `cat filename`
- before shutting down, pound removes this file. however, this may not be possible if it switches to privileges of another user after startup (i.e. at least one of
UserorGroupare set in the configuration file) and the file is stored in a directory whose permissions forbid write access for that user. so plan your permissions, bestie ๐ญ
RegexType posix | pcre | perlโ sets the type of regular expressions to use in request matching statements.posixselects POSIX extended regular expressions andpcreorperlselect Perl-compatible regular expressions. the latter requires compile-time support. the selected regular expression type remains in effect until nextRegexTypestatement or end of the configuration file, whichever occurs first ๐ชReserveFD [-worker] Nโ reserveNadditional file descriptors for the purpose of computing the capacity of the incoming connection queue. if the-workeroption is given, increases number of file descriptors per worker thread (default value:2). otherwise, increases number of reserved global descriptors (default value:3) ๐Supervisor boolโ when running in daemon mode, start a supervisor process first. this process will monitor the subordinate pound process, restarting it if it fails. she's got a babysitter ๐Threads Nโ this statement, retained for backward compatibility with previous versions of pound, is equivalent to:
WorkerMinCount N
WorkerMaxCount N
oldie but a goodie ๐ซถ
WorkerIdleTimeoutSEC โ how long a worker thread is allowed to sit around doing nothing before it gets dropped ๐ด default is30seconds. The whole saga is in WORKER MODEL above if you want the lore.WorkerMinCountN โ the minimum number of worker threads that are always running, no matter what ๐ซถ default is5. Again, WORKER MODEL above has the full breakdown.WorkerMaxCountN โ the ceiling, bestie. Max number of worker threads ever ๐ฆ default is128. Yep, WORKER MODEL above, go read it.WorkerStackSizeN โ worker stack size, in bytes ๐ default is262144.
๐ Privilege selection ๐
Like, who even are you, pound? This is where you tell it ๐
User "user_name"โ pick the user pound runs as. It must already exist in the system user database, no cap ๐Group "group_name"โ same vibe, but for the group. It must be defined in the system group database ๐RootJail "directory"โ tells pound which directory tochrootto at runtime ๐ iconic security behavior, fr.
๐ธ SSL engine settings ๐ธ
SSLEngine "name"โ use an OpenSSL hardware acceleration card called name โก heads up though: this is only available if OpenSSL-engine is installed on your system, so vibe check that first.
๐ฆ Logging ๐ฆ
The part where pound tells you everything that happened ๐ฌ
Anonymiseโ (alt spellingAnonymizeis also accepted, we're flexible ๐) replaces the last byte of the client address with0for logging purposes. Default behavior: log the client address in full. Privacy moment โจLogFacilityident โ which log facility to use. ident is one of these, and yes you have to spell them right:
auth, authpriv, cron, daemon, ftp, kern, lpr, mail, news, syslog, user, uucp, local0 through local7.
Default value is daemon ๐ป and plot twist: if you use a - (dash) for the facility name, pound logs to stdout/stderr instead, which is lowkey great for debugging.
LogFormat "name" "format_def"โ define your own HTTP log format ๐ name is a string that uniquely identifies this format, and format_def is the format string definition. Detailed breakdown lives below in REQUEST LOGGING, so scroll down bestie.LogLevel nโ set the logging level using the built-in format indices ๐๏ธ:0โ no logging at all (silence, luxury)1โ regular logging (this is the default)2โ extended logging (shows the chosen backend server as well)3โ Apache-like format (Combined Log Format with Virtual Host)4โ same as3but without the virtual host information5โ same as3but with info about the Service and Backend used
This value can be overridden for specific listeners ๐ And yeah, REQUEST LOGGING below has the full description.
LogLevel "name"โ pick a named format for logging HTTP requests instead of a number ๐ซ Name can be either one of the five built-in format names โnull,regular,extended,vhost_combined,combined, ordetailedโ matching the numeric indices we just talked about, or a format name you defined earlier withLogFormat. See REQUEST LOGGING for the deep dive.LogTag "string"โ sets the string used to tag log messages ๐ท๏ธ used when log output goes to syslog. Default is the name pound was started with.ForwardedHeadername โ defines the name of the HTTP header that carries the list of proxies the request has passed through ๐ฌ it's used to report the originator IP address when logging. See the%aspecifier description in REQUEST LOGGING. Default isX-Forwarded-For.TrustedIPโ defines a list of trusted proxy IP addresses, used to determine the originator IP ๐ see the%aspecifier description in REQUEST LOGGING for the detailed discussion.
This statement is a special form of ACL statement, described below. It can appear as an anonymous ACL (section), a file reference, or as an ACL reference. In anonymous ACL form, it's followed by a list of one or more CIDRs, each on its own line. The End keyword terminates the statement, like this:
TrustedIP
"127.0.0.1/8"
"10.16.0.0/16"
End
In file reference form, the statement looks like:
TrustedIP -file "filename"
or
TrustedIP -filewatch "filename"
and the list of IP addresses gets read from the file filename ๐ when you use -filewatch, the file is watched for changes and reloaded when they happen ๐๏ธ
In ACL reference form, this statement takes a single argument โ the name of the access control list defined earlier with the ACL statement, like:
TrustedIP "proxy_addresses"
๐ Timeouts ๐
Everything in here is about pound's patience level. It has some, but not infinite ๐ซ
Aliven โ how often pound checks for resurrected backend hosts ๐ป default:30seconds. Generally it's a good idea to set this as low as possible โ it'll find resurrected hosts faster. But bestie, beware: set it too low and it'll consume resources ๐ญ so, like, balance.Clientn โ how long pound waits for a client request โณ default:10seconds. After that long with the client sending no data, pound closes the connection. Bump it higher if your clients time out on a slow network or an overloaded server. Bump it lower if you start getting DOS attacks or run into problems with IE clients ๐ This value can be overridden for specific listeners.ConnTOn โ how long pound should wait for a connection to the backend, in seconds ๐ default: theTimeOutvalue. This value can be overridden for specific backends.Gracen โ how long pound keeps answering existing connections after receiving anINTorHUPsignal ๐๏ธ default:30seconds. The configured listeners are closed immediately. You can bypass this behavior entirely by stopping pound with aTERMorQUITsignal โ in that case the program exits without any delay ๐โโ๏ธ๐จTimeOutn โ how long pound waits for a response from the backend, in seconds โฑ๏ธ default:15seconds. This value can be overridden for specific backends.WSTimeOutn โ how long pound waits for data from either the backend or the client on a connection upgraded to a WebSocket, in seconds ๐งฆ default:600seconds. This value can be overridden for specific backends.WatcherTTLN โ if conditions with the-filewatchflag are used, and your underlying OS doesn't provide a file system monitoring API (inotifyorkqueue), this directive defines the interval between two successive checks of file modification times ๐ (yes, it's polling, we know, it's doing its best).
๐ Header manipulation ๐
CombineHeaders ... Endโ declare the names of headers that can appear multiple times in a message and should be combined into one value ๐งต header names go one per line betweenCombineHeadersandEnd. See MULTI-VALUE HEADERS for the detailed discussion of this feature.HeaderOptionopt... โ sets the default options for header addition ๐๏ธ opt is one of:noneโ disable additional headersforwardedโ enable addingX-Forwarded-For,X-Forwarded-Proto, andX-Forwarded-Portheaderssslโ enable passing information about SSL certificates in variousX-SSL-*headers
The option may be prefixed with no-, which disables the corresponding header group ๐ซ The default is:
HeaderOption forwarded ssl
This setting can be overridden on a per-listener basis. See the HeaderOption directive description in the HTTP Listener section, plus BUILT-IN HEADERS, for the detailed discussion of the various header modification directives and their effects ๐ซ
๐ง File inclusion ๐ง
IncludeDir "dir"โ sets the include directory ๐ this is where pound looks for relative file names that appear in other configuration directives (e.g.Include). The default value is the system configuration directory as set at compile time โ you can check its value in the output ofpound -V๐ This initial value can be changed on the command line using the-W include-dir=nameoption, or reset to the current working directory using the-W no-include-diroption (see the discussion of-Wbelow).
The directory set by the IncludeDir statement remains in effect until the next IncludeDir statement is encountered, or end of input โ whichever happens first ๐
Include "file"โ include the file as though it were part of the configuration file ๐ if file is a relative file name, it'll be looked up in the include directory (see above). If file is a shell globbing pattern (glob(7)), all regular files matching that pattern get included. The list of files will be sorted lexicographically ๐ค
This directive is allowed both at the topmost level and in any subsections of the configuration file. Versatile icon ๐
โญ Access control lists โญ
ACL "name"โ define a named access control list (ACL) ๐ an ACL is a list of network addresses in CIDR notation, one address per line, terminated with anEnddirective on a line by itself. Like so:
ACL "secure"
"192.0.2.0/26"
"203.0.113.0/24"
End
The Include directive is allowed within an ACL ๐ซถ
Named ACLs can be used in Service definitions to make services available from certain IP addresses only ๐
ACL "name" -file "filename"โ same as above, but reads the ACL definition from the file filename ๐ the name is taken relative to theIncludeDirdirectory, unless it starts with a slash.
The ACL file should contain one CIDR per line. Empty lines and comments are allowed โ
ACL "name" -filewatch "filename"โ same as above, but filename gets watched for changes during runtime ๐๏ธ once a change is detected, the file is reloaded and the ACL updated ๐
If possible, this uses an OS-specific API (inotify(7) on GNU/Linux and kqueue(7) on *BSD systems). On systems that don't provide such an API, the file is checked for changes periodically โฐ the interval between two successive checks is defined by the WatcherTTL setting.
๐ Detached conditions ๐
Condition "NAME"
...
End
Saves away a set of conditional statements for further evaluation ๐พ the ellipsis represents a sequence of conditional statements as described in section Service Matching Directives. The detached condition can be used wherever a normal conditional is allowed, using this construct:
Eval "NAME"
Detached conditions are evaluated at most once ๐ง the result of the evaluation is cached and used by all subsequent Eval statements that refer to the same NAME. Efficiency ate, fr fr ๐ฅ
๐ Control socket ๐
okay so bestie, pound can also be told to listen for management requests โ like, it'll open up a whole little admin channel so you can peek at info about the running instance, change the state of configured listeners, services, and backends, that kind of thing. these requests get issued with the poundctl(8) utility. ๐
normally it's a UNIX socket that talks to the management interface, and you set it up with the Control statement. this statement comes in two forms, bestie. โจ
in inline form, it takes a single argument โ the name of the UNIX socket file to create and listen on. for example:
Control "/run/pound.sock"
the file will be owned by the user that started pound (normally root) and will have mode 0600. ๐
the block form lets you specify the file mode and, to a certain extent, the socket file ownership:
Control
Socket "/run/pound.sock"
Mode 660
ChangeOwner true
End
the substatements are:
Socket filenameโ specifies the name of the socket file to use. this is the only mandatory statement in the block form. ๐Mode octalโ sets the mode of the socket file.ChangeOwner boolโ this one only kicks in if at least one of theUserorGroupglobal statements is used. when set totrueit changes the owner of the socket file to whatever those two statements say. ๐ซถ
it's also possible to have pound listen for management requests on an INET or INET6 address โ see the description of the Control statement in section Backend definitions, below. ๐
๐ธ HTTP Listener ๐ธ
an HTTP listener defines an address and port that pound will listen on for HTTP requests. the listener declaration begins with the keyword ListenHTTP on a separate line. the keyword may be followed by a quoted string supplying the listener name โ that name is a unique label that identifies the listener. ๐ท๏ธ
all configuration directives enclosed between ListenHTTP and End are specific to a single HTTP listener. at the very least you must specify the IP address and, optionally, the port for each listener. The following directives are available:
Address addressโ the address pound listens on. this can be a numeric IP address, or a full pathname of a UNIX socket. if neither this directive norSocketFrom(see below) is given,::0is assumed. ๐Port portโ the port number or service name pound listens on. if not given, the default port number is assumed, i.e.80, forListenHTTP, and443, forListenHTTPS(see below). ๐ขSocketFrom "pathname"โ read the socket to listen on from the UNIX socket given as argument. if this parameter is supplied, neitherAddressnorPortmay be used. this directive is intended for testing pound. ๐งชxHTTP nโ defines which HTTP verbs are accepted. the possible values are:0(default) โ accept only standard HTTP requests (GET,POST,HEAD).1โ additionally allow extended HTTP requests (PUT,PATCH,DELETE).2โ additionally allow standard WebDAV verbs (LOCK,UNLOCK,PROPFIND,PROPPATCH,SEARCH,MKCOL,MOVE,COPY,OPTIONS,TRACE,MKACTIVITY,CHECKOUT,MERGE,REPORT).3โ additionally allow MS extensions WebDAV verbs (SUBSCRIBE,UNSUBSCRIBE,NOTIFY,BPROPFIND,BPROPPATCH,POLL,BMOVE,BCOPY,BDELETE,CONNECT).
Client nโ override the globalClienttime-out value. โฑ๏ธCheckURL "pattern"โ define a pattern that every request sent to this listener must match. a request that doesn't match is considered illegal. by default pound accepts all requests (i.e. the pattern is".*"), but you're free to limit it to something more reasonable. heads up: this applies only to the request path โ pound will still check that the request is syntactically correct. ๐ต๏ธConstant "NAME" [OPTIONS] ["VALUE"]โ defines a configuration constant for use in string expansions and Lua code. seeConstantin section GLOBAL DIRECTIVES for the full breakdown of its arguments and what they mean. per-listener constants augment global ones. if the name of a per-listener constant coincides with the name of a globally defined one, the former overrides the latter for all services defined in that listener. โจErrorFile NNN "filename"โ read the content to be returned along with the HTTP status code NNN from the file filename. the file is read exactly once, at program startup. ๐ the file can begin with one or more HTTP headers, separated from the actual body with an empty line (i.e., two newline characters). these headers get returned along with the response โ this can be used, in particular, to change the response content type, which istext/htmlby default. allowed values for NNN are:400,401,404,405,413,414,500,501,503.ErrNNN "filename"โ this statement is equivalent to
ErrorFile NNN "filename"
see above for the allowed values of NNN.
โ ๏ธ this form is provided for compatibility with previous versions of the program. it will be removed in future releases โ so maybe don't build your whole personality around it, bestie. ๐
RewriteErrors boolโ if bool istrue, this enables backend error response rewriting, i.e. replacing the content of error responses returned by a regular backend with a custom error text defined by theErrorFilestatement. see ERROR RESPONSE REWRITING for a detailed discussion of this feature. the same statement can also appear in aServiceblock, in which case it overrides the settings of the listener. ๐LineBufferSize Nโ sets the line buffer size. the line buffer is used when reading the request line, the backend response status line, and header fields in both requests and responses. the default is inherited from the global setting. ๐MaxRequest nโ maximum allowed size of incoming request. all requests will be limited to these many bytes. if a request contains more data than allowed, an error413is returned. default: unlimited. ๐ซMaxURI nโ maximum allowed length of a URI. if the URI of a request is longer thannbytes, an error414is returned. default: unlimited. ๐RewriteLocation 0|1|2โ if set to1, force pound to change theLocation:andContent-location:headers in responses. if they point to the backend itself or to the listener (but with the wrong protocol), the response gets changed to show the virtual host in the request. default:1(active). if the value is set to2, only the backend address is compared โ this is useful for redirecting a request to an HTTPS listener on the same server as the HTTP listener. ๐งญRewriteDestination boolโ if set totrue, force pound to change theDestination:header in requests. the header is changed to point to the backend itself with the correct protocol. default:false. ๐ฏLogLevel nโ log HTTP requests using built-in formatn. this statement configures logging specific for this listener, overriding the globalLogLevelsetting. see REQUEST LOGGING for a detailed discussion. ๐LogLevel "name"โ select a named format for logging HTTP requests. name can be either one of five built-in format names (null,regular,extended,vhost_combined,combined, ordetailed), or a format name defined earlier via theLogFormatdirective. this statement configures logging specific for this listener, overriding the globalLogLevelsetting. see section REQUEST LOGGING for a detailed discussion. ๐๏ธForwardedHeader nameโ defines the name of the HTTP header that carries the list of proxies the request has passed through. it's used to report the originator IP address when logging. see the description of the%aspecifier in REQUEST LOGGING. this statement overrides theForwardedHeaderdirective from the global scope. the default isX-Forwarded-For. ๐จTrustedIPโ defines a list of trusted proxy IP addresses, which is used to determine the originator IP. see the description of the%aspecifier in REQUEST LOGGING for a detailed discussion. this statement overrides theTrustedIPdirective from the global scope. ๐ก๏ธService [ "name" ]โ this defines a private service (see below for the service definition syntax). this service is used only by this listener. the optional name supplies the label that can be used inpoundctl(8)requests to identify the service. this label must be unique among all services within the enclosing listener, or, for global services, within the configuration. ๐งACME "directory"โ serve ACME challenge requests from the given directory. see section ACME below. ๐HeaderOption opt...โ modifies global header addition options for this listener. global options are set by theHeaderOptiondirective in the global scope and default toforwarded ssl. opt is one of:allโ enable all additional headers.noneโ disable all additional headers.forwardedโ enable addingX-Forwarded-For,X-Forwarded-Proto, andX-Forwarded-Portheaders.sslโ enable passing information about SSL certificates in variousX-SSL-*headers.
each option except none can be prefixed with no- to revert its meaning. for example, to disable adding the X-SSL-* headers for a listener, you'd use:
HeaderOption no-ssl
see the description of the HeaderOption directive in the GLOBAL DIRECTIVES section, and section BUILT-IN HEADERS, for a detailed discussion of the various header modification directives and their effect. ๐
โจ String expansion โจ
some of the statements described below take as their arguments string values that undergo several expansions before use. these expansions are as follows:
๐ฆ Backreference expansion ๐ฆ
a backreference is a construct that refers to a parenthesized group within a regular expression matched by one of the service matching directives (see the section Service Matching Directives, below). during backreference expansion, each occurrence of such a construct in a string is replaced with the actual value of that parenthesized group. ๐
and that's where this part leaves us, bestie โ the rest comes at you in the next chunk, no cap! ๐ซถ
k so backreferences come in exactly two shapes, and bestie you're gonna want both memorized ๐
The construct $N (where N is a decimal number) points at the Nth parenthesized subexpression of the most recently matched statement, and $N(M) points at the Nth parenthesized subexpression of the Mth recently matched statement. Subexpression numbering starts at 1 โ and yes, $0 is special, it refers to the entire matching string ๐ Numbering of matched statements starts at 0.
Conditional statements can also be tagged with the -tag T option. When you're referring to a subexpression inside a tagged statement like that, you use the $N(T) form. iykyk ๐
For example, given the following statements:
Host -tag "host" -re "www\.(.+)"
Header -tag "type" -re -icase "^Content-Type: *(.*)"
Path -tag "dir" "^/static(/.*)?"
then $1 or $1(dir) refers to the subgroup of Path, $1(1) or $1(type) refers to that of Header, and $1(2) or $1(host) refers to that of Host. literally so satisfying when it clicks โจ
Curly braces exist so you don't accidentally parse whatever text fragment follows the reference as if it were part of the reference. This is the move when the reference is immediately followed by a decimal digit or an opening parenthesis โ as in: "${1}(text)". ๐ซถ
To insert a literal dollar or percent sign in url, use $$ or $%, correspondingly.
๐ฆ Request accessor interpretation ๐ฆ
A request accessor is %[name], where name is the part of the incoming request you wanna peek at. Accessors get interpreted and swapped out for the corresponding piece of the actual request. Some accessors take an argument, and that argument goes after the accessor name, separated from it by one or more whitespace characters.
Here's the whole roster, no gatekeeping:
const NAME [ENCODE]โ the configuration constant NAME (see Constant in the section GLOBAL DIRECTIVES, above). The optional ENCODE says which encoding to represent it in, and its possible values are:urlโ the request URL.pathโ the request path.queryโ the query part.param NAMEโ the value of the query parameter NAME.header NAMEโ the value of HTTP header NAME.hostโ the hostname part of the Host header. If the latter doesn't include a port number, this is equivalent to%[header host]. ๐portโ if the value of the Host header includes a port number,%[port]evaluates to that port number with the leading colon character. Otherwise it evaluates to an empty string.remoteip FWDโ expands to the request originator IP address. If FWD is0, it expands to the real IP; otherwise it expands to the IP determined from theX-Forwarded-For(or similar) header. ๐งuserโ if the request bears theAuthorization: Basicheader, this expands to the user name extracted from its value. Otherwise its expansion is empty.
๐ Modification directives ๐
The following directives modify the incoming request before it gets passed to the selected service. These get the full deep-dive treatment in section REQUEST MODIFICATION, below โ but here's the quick tour, bestie:
DeleteHeader [options] "pattern"โ removes certain headers from the incoming requests. All occurrences of the matching header will be removed, not just one, okay? The options define how to match pattern:-begโ exact match at the beginning of the header (prefix match).-caseโ case-sensitive comparison.-containโ match a substring of the header.-endโ exact match at the end of the string (suffix match).-exactโ exact string match.-icaseโ case-insensitive comparison.-pcreor-perlโ use a Perl-compatible regular expression (requires compilation-time support โ ๏ธ).-posixโ use a POSIX extended regular expression.-reโ use regular expression matching, as set by theRegexTypestatement.
The default is -re, meaning pattern is treated as a regular expression as defined by the RegexType statement. periodt ๐
SetHeader "header: value"โ adds a header to the request passed to the backend server. The argument undergoes string expansion as described above. The expanded value must be a valid header line โ no funny business ๐ธSetURL "value"โ sets the URL part of the request. The argument is subject to string expansion.SetPath [-encode] "value"โ sets the path part of the request. The argument is subject to string expansion and, if-encodeis given, percent-encoding.SetQuery "value"โ sets the query part of the request. The argument is subject to string expansion.SetQueryParam "name" [-encode] "value"โ modifies the query. Sets the query parameter name to value. The value argument is subject to string expansion and, if-encodeis given, percent-encoding. ๐Rewrite [request|response] ... [ Else ... ] Endโ conditionally modifies the request or the response depending on whether it matches certain conditions. If the argument is omitted, request is assumed.
Request modification is described in detail in section REQUEST MODIFICATION DIRECTIVES.
Response modification is covered by section RESPONSE MODIFICATION. ๐ฆ
๐ Compatibility directives ๐
The following directives are retained for compatibility with previous versions of pound. They will be removed in future releases โ so like, don't get attached, bestie ๐ญ
HeaderAdd "header: value"โ same asSetHeader.AddHeader "header: value"โ same asSetHeader.HeaderRemove "pattern"โ same asDeleteHeader.HeadRemove "pattern"โ same asDeleteHeader.
๐ธ HTTPS Listener ๐ธ
okay so an HTTPS listener defines an address and port that pound will listen on for HTTPS requests. The listener declaration begins with the keyword ListenHTTPS on a separate line. The keyword may be followed by a quoted string supplying listener name. This name is a unique label that identifies the listener.
All configuration directives enclosed between ListenHTTPS and End are specific to a single HTTPS listener. At the very least you must specify an address, optionally a port, and a server certificate for each listener. All directives defined for HTTP listeners are applicable to HTTPS listeners as well. The following additional directives are also available:
Cert "filename"โ specify the server certificate. Filename is either a certificate file name, or the name of a directory containing certificate files.
Certificate file is a file containing the certificate, possibly a certificate chain and the signature for this server, in that order.
This directive is mandatory for HTTPS listeners.
Please note that multiple Cert directives are allowed if your OpenSSL version supports SNI. In such cases, the first directive is the default certificate, with additional certificates used if the client requests them.
The ordering of the directives is important: the first certificate where the CN matches the client request will be used, so put your directives in the most-specific-to-least specific order (i.e. wildcard certificates after host-specific certificates).
Cert directives must precede all other SSL-specific directives.
ClientCert 0|1|2|3 depthโ ask for the client's HTTPS certificate:0โ don't ask (default),1โ ask,2โ ask and fail if no certificate was presented,3โ ask but do not verify. Depth is the depth of verification for a client certificate (up to9). The default depth limit is9, allowing for the peer certificate and additional9CA certificates that must be verified.Disable SSLv2|SSLv3|TLSv1|TLSv1_1|TLSv1_2โ disable the protocol and all lower protocols as well. This is due to a limitation in OpenSSL, which does not support disabling a single protocol. For example,Disable TLSv1would disableSSLv2,SSLv3andTLSv1, thus allowing onlyTLSv1_1andTLSv1_2.Ciphers [typeopt] "ciphers" ...โ defines the list of ciphers (forTLSv2and below) or ciphersuites (forTLSv3) that will be accepted by the SSL connection. The typeopt option defines how to interpret the ciphers argument that follows it:-cipherlistโ sets the list of ciphers that affects onlyTLSv2and prior protocols. This is the default.-ciphersuitesโ sets the list ofTLSv3ciphersuites. It will be combined with anyTLSv1.2and below cipher lists that may have been configured.
Refer to ciphers(1), for a discussion of the ciphers argument.
SSLHonorCipherOrder boolโ if set totrue, the server will broadcast a preference to use ciphers in the order supplied in theCiphersdirective. If the value isfalse, the server will accept any cipher from theCipherslist. Default value isfalse.SSLAllowClientRenegotiation 0|1|2โ if this value is0, client initiated renegotiation will be disabled. This will mitigate DoS exploits based on client renegotiation, regardless of the patch status of clients and servers related to "Secure renegotiation". If the value is1, secure renegotiation is supported. If the value is2, insecure renegotiation is supported, with unpatched clients. This can lead to a DoS and a Man in the Middle attack! The default value is0.CAlist "filename"โ set the list of "trusted" CA's for this server. The filename is the name of a file containing a sequence of CA certificates (PEM format). The names of the defined CA certificates will be sent to the client on connection.VerifyList "filename"โ set the CA (Certificate Authority). The filename is a file that contains the CA root certificates (in PEM format).
Please note: there is an important difference between the CAlist and the VerifyList. The CAlist tells the client (browser) which client certificates it should send. The VerifyList defines which CAs are actually used for the verification of the returned certificate.
CRLlist "filename"โ set the CRL (Certificate Revocation List) file. The filename is a file that contains the CRLs (in PEM format).NoHTTPS11 0|1|2โ behave like an HTTP/1.0 server for HTTPS clients. If this value is0, disable the check. If the value is1, do not allow multiple requests on SSL connections. If the value is2(default), disable multiple requests on SSL connections only for clients whoseUser-Agentheader contains substringMSIE. This may be required as a work-around for a bug in certain versions of IE.
๐ฆ Tunnel ๐ฆ
Tunnel is a special listener that defines a transparent, protocol-agnostic communication channel between two sockets. Anything sent to one end of the tunnel appears at its other end and vice-versa.
Tunnels can be thought of as a software equivalent of port forwarding.
Syntactically a tunnel is declared using the following construct:
Tunnel [ "ID" ]
Address IPADDR
Port PORT
Backend
Address IPADDR
Port PORT
End
End
๐ Service ๐
A service is a definition of which backend servers pound will use to reply to incoming requests. A service may be defined as part of a listener (in which case it will be used only by that listener), or globally (which makes it available to all listeners). pound selects a listener based on user-supplied conditions that analyze the incoming request URL and/or headers. It will always scan listener-specific services first. If none matches, it will try the global ones. Services are tried in the same order as they are defined in configuration.
All configuration directives enclosed between Service and End are specific to a single service. They can be subdivided into two categories: service matching directives and backend definitions.
โจ Service Matching Directives โจ
These directives determine whether a particular request should be handled by this service. When a request arrives, each service is considered in turn (first services defined within the listener that received the request, then the ones defined in global scope). The first service that matches the request will be used. If no service matches, a 503 "Service unavailable" error is returned.
Unless explicitly stated in the configuration file, all matching directives are joined by a boolean AND.
A service with no matching directives always matches.
ACL [-forwarded] "name"โ match the source IP address against the named ACL. The ACL must have been defined earlier (see theACLstatement inGLOBAL DIRECTIVESsection above). If the originating IP doesn't match any CIDR from the list, then this service will be skipped and next one tried.
The -forwarded keyword, if present, instructs the program to deduce originator IP from the X-Forwarded-For header, instead of using the actual source IP address. See the description of %a specifier in REQUEST LOGGING, for a detailed discussion of how the IP is determined.
ACL [-forwarded]โ this statement defines an unnamed ACL to match the source IP against. See above for the meaning of the optional argument. This line must be followed by one or more lines defining CIDRs, as described in theGLOBAL DIRECTIVESsection above. The ACL definition is finished with aEndkeyword on a line by itself.
Semantically it is equivalent to the named ACL reference described above.
ACL [-forwarded] -file "filename"โ same as above, but reads ACL definition from file filename. The name is taken relative to theIncludeDirdirectory, unless it starts with a slash.
The ACL file should contain one CIDR per line. CIDRs need not be quoted. Empty lines and comments are allowed.
The -forwarded optional keyword has the same meaning as described above.
ACL [-forwarded] -filewatch "filename"โ same as above, but filename will be watched for changes during the runtime. Once a change is detected, the file will be reloaded and the ACL updated.
If possible, this uses OS-specific filesystem monitoring API (inotify(7), on GNU/Linux and kqueue(7) on *BSD systems). On systems that don't provide such API, the file will be checked for changes periodically, in intervals defined by the WatcherTTL setting.
BasicAuth [option] "filename"โ evaluates to true if the incoming request passes basic authorization as described in RFC 7617. filename is the name of a plain text file containing usernames and passwords, created withhtpasswd(1)or similar utility. Unless it starts with a slash, it is taken relative to theIncludeDirdirectory. The file is read at startup and cached in memory, so that authorizations normally don't result in disk operations.poundwill monitor the file using the same mechanism as forACL -filewatch(see above), and reload it when it changes.
See the section entitled BASIC AUTHENTICATION, for a detailed discussion.
For consistency with other conditions, BasicAuth accepts an option: -file or -filewatch. The latter is the default and normally need not be specified explicitly. The former can be used to disable file change monitoring.
ClientCert "filename"โ evaluates to true if the client presented the certificate matching that from the file filename (PEM format).
This conditional cannot be used in standalone services (i.e. services that are defined in global scope), or if the ListenHTTPS section that hosts the service has the ClientCert statement on its own.
๐ the directives (part 2, electric boogaloo)
ContentCapture SIZE
okay so this one turns ON capturing of the incoming request content. if the content length is less than or equal to SIZE, the content gets captured and stashed away while it's being sent over to the backend. then that saved value is handed to your Lua procedures (the ones invoked by LuaMatch, LuaModify, and LuaBackend) through the variable http.req.body. ๐ซถ
Eval "NAME"
evaluates the detached condition called NAME. and like โ the condition has to be defined by a Condition statement that comes before this one (peep above, section GLOBAL DIRECTIVES โจ). bonus: if multiple Eval statements point at the same NAME, the condition gets evaluated exactly once and the result is reused by the later statements. efficiency icon behavior, no cap ๐
Header [options] "pattern"
the request must contain at least one header matching the given pattern. by default, pattern is treated as a case-insensitive extended regular expression โ but you can switch that up with options, described below. ๐ฆ
Header "Name" [options] "pattern"
alt form of the Header statement. in this form, pattern is matched against the value of each header that has the supplied Name. for example:
Header "Content-Type" -beg "text/"
Host [options] "hostname"
the request must contain a Host header whose value matches hostname. if you don't pass any options, case-insensitive exact match is assumed โ meaning this construct is literally equivalent to:
Header "Host" -exact "hostname"
see below for the whole discussion of options and how they affect matching ๐
this statement exists to make virtual host handling easier. check the EXAMPLES section.
Method [options] "pattern"
matches the request method. simple as that, periodt.
Path [options] "pattern"
matches the path part of the incoming request. ๐
Query [options] "pattern"
matches the query part of the incoming request. important: the argument must be properly percent-encoded if it contains whitespace or other non-printable characters. don't skip that part bestie ๐
QueryParam "name" [options] "pattern"
matches the value of the query parameter name.
StringMatch "string" [options] "pattern"
expands string the way String expansion describes (see below โฌ๏ธ) and matches the resulting value against pattern.
TBF "key" rate burst
defines a token bucket filter for that service. there's a whole discussion below in the section entitled REQUEST RATE if you want the full lore. arguments are:
- key โ a string identifying the bucket to use. it's subject to string expansion (see the subsection String expansion, above).
- rate โ allowed request rate, requests per second. fractional rates are allowed too:
1/2(one request each two seconds),3/500ms(three requests each 500 milliseconds), etc. ๐ - burst โ burst size, i.e. maximum number of tokens the bucket can contain.
the TBF conditional evaluates to true if the request conforms to the defined rate policy, and false otherwise. slay.
URL [options] "pattern"
matches the URL of the incoming request. by default, pattern is treated as a case-sensitive extended regular expression โ yes, different from Header, don't come for me ๐ญ โ and this can be changed by the options, described below.
โจ the options โจ
the options argument in all those directives above is how you pick the comparison method. it's made up of zero or more option flags from this list:
-begโ exact match at the beginning of string (prefix match).-caseโ case-sensitive comparison.-containโ match substring.-endโ exact match at the end of string (suffix match).-exactโ exact string match.-fileโ treat pattern as the name of a file to read patterns from. if the name is relative, it gets looked up in the include directory (see the discussion of theIncludeDirdirectory above). patterns are read from the file line by line. leading and trailing whitespace is removed. empty lines and comments (lines starting with#) are ignored. ๐ง-filewatchโ same as-file, except the file is monitored for changes and reloaded as soon as they happen. if possible this uses an OS-specific API (inotify(7)on GNU/Linux andkqueue(7)on *BSD systems). on systems that don't provide such an API, the file gets checked for changes periodically. the interval between two successive checks is defined by theWatcherTTLsetting.-icaseโ case-insensitive comparison.-pcreor-perlโ use Perl-compatible regular expression (requires compile-time support). this overrides globalRegexTypesettings. ๐ฅ-posixโ use POSIX extended regular expression. this overrides globalRegexTypesettings.-reโ use regular expression matching, as set by theRegexTypestatement.-tag "T"โ tag the statement with stringT. that string can be used in subsequent modification statements to refer to a subexpression of this statement's pattern, using the syntax$I(T), whereIis the ordinal number of the subexpression.
for example, the following matches any request whose Host header begins with "www." (case-insensitive):
Host -icase -beg "www."
๐ก also:PathandQueryParamdirectives accept the option-decode, which makes the obtained value undergo percent-decoding before the comparison. handy, iykyk.
๐ Compatibility directives
these next ones are here purely for backward compatibility with older versions of pound. they will be removed in future versions โ so like, don't build your whole personality on them, bestie. ๐ซถ
HeadRequire "pattern"โ same asHeader.HeadDeny "pattern"โ a shortcut for:
Not header "pattern"
in other words: the request may not contain any header matching the given pattern. see the Negation section, below.
๐ Negation
prefix any of the directives discussed above with not and it reverts the sense of the comparison. easy. for example:
Not url "^/static/"
will match any request whose URL does not begin with "/static/".
negating compound statements is allowed too, e.g.:
Not ACL
"192.0.2.0/26"
"203.0.113.0/24"
End
โญ Match statement
a Match statement lets you group matching directives using arbitrary boolean operations. the syntax is:
Match OP
directives...
End
where OP is AND or OR (case-insensitive ๐), and directives stands for any number of the matching directives discussed above (including the Match directive itself โ yes, it's recursive, she contains multitudes).
prefixing a Match directive with the word not reverts its result.
Match directives can be nested to any depth. literally any. go wild.
technically speaking, an implicit Match AND block is created around all the unenclosed matching directives on the top level of a Service block. so it was there the whole time ๐
๐ฆ Modification directives
the following directives modify the incoming request before it gets passed to the selected backend. full deets in the section REQUEST MODIFICATION, below.
DeleteHeader [options] "pattern"
removes matching headers from the incoming request. for the list of available options and their effect on matching, see the description of the DeleteHeader statement in section HTTP Listener, subsection Modification directives, above. ๐
SetHeader "header: value"
adds the defined header to the request passed to the backend server.
SetURL "value"
sets the URL part of the request.
SetPath [-encode] "value"
sets the path part. if you give it the -encode option, value gets percent-encoded after the usual expansions.
SetQuery "value"
sets the query part.
SetQueryParam "name" [-encode] "value"
modifies the query. sets the query parameter name to value. if you give it the -encode option, value gets percent-encoded after the usual expansions.
Rewrite [request|response] ... [ Else ... ] End
conditionally modifies request or response depending on whether it matches certain conditions. if the argument is omitted, request is assumed.
request modification is described in detail in section REQUEST MODIFICATION DIRECTIVES.
response modification is covered by section RESPONSE MODIFICATION.
RewriteErrors bool
if bool is true, this enables backend error response rewriting for this service โ i.e. replacing the content of error responses returned by a regular backend with a custom error text defined by the ErrorFile statement in the listener. see ERROR RESPONSE REWRITING for the full deep dive on this feature. ๐
this statement overrides settings of the enclosing listener, so that:
RewriteErrors off
appearing inside a Service will disable that feature for that particular service. sneakyyyy ๐
๐ Backend definitions
Balancer random | iwrr
defines the load-balancing strategy to use. possible arguments are: random, to use the weighted random balancing algorithm, and iwrr, meaning interleaved weighted round robin balancing. see REQUEST BALANCING, above, for the detailed discussion of these strategies.
this directive overrides the Balancer directive in global scope.
Backend
the directives enclosed between Backend and the following End directives define a single backend server (details below โฌ๏ธ). you can define multiple backends per service, in which case pound will distribute the load between them according to the selected balancer strategy. load balancing era ๐
Control
enables a special control backend โ a management interface that returns info about the running pound server, makes it possible to change the state of configured listeners, services and backends, and provides other management facilities. the management interface is discussed in detail in section Control socket, above. the Control backend provides remote access to this interface.
โ ๏ธ be careful to properly protect the control interface using HTTPS, ACLs and/or basic authorization, e.g.:
ListenHTTPS
Address 192.0.2.1
Port 443
Cert "/etc/ssl/priv/example.pem"
Service
Not BasicAuth "pound/htpasswd"
Rewrite response
SetHeader "WWW-Authenticate: Basic realm=\"Restricted access\""
End
Error 401
End
Service
ACL "secure"
Control
End
End
like, fr fr, lock that thing down ๐
๐ธ Emergency
The directives you tuck between Emergency and the next End define an emergency backend server (more deets below ๐). You can define as many emergency servers as you want, no cap. A backend gets picked from the emergency pile only when all your regular backends are down. It's giving "break glass in case of emergency" ๐
๐ Error STATUS [FILE]
A special-case backend that just returns an HTTP error page. The STATUS argument is the HTTP status code. The optional FILE argument is the name of a disk file holding the error page content โ if you pass it, the file gets handled exactly as in the ErrorFile directive (go look that one up, iykyk). If you don't pass it, the response content is figured out the usual way: first the ErrorFile STATUS statement for the enclosing listener gets consulted. If that's not there either, you get the default error page. ๐ซถ
The Error directive is lowkey perfect for a catch-all service โ one that spits out an error page when no service matching the incoming request was found. Peep the EXAMPLES section for the details โจ
๐ Metrics
A special backend type that implements Openmetrics protocol output. There's a whole section called Metrics below if you wanna dive in ๐
๐ Redirect [code] "url"
This is a special type of backend, bestie. Instead of sending the request off to a backend, pound replies immediately with a redirection to the given URL. Slay.
You're allowed to define multiple redirectors in a service, and even mix them with regular backends โ but honestly there's little if any justification for that, so it's deprecated ๐
The optional code can be one of: 301, 302 (the default), 303, 307, or 308.
The redirection destination is determined by the actual url you specify, and it's expanded as described in String expansion above.
For compatibility with previous pound versions, if no $N references are found in url, this logic kicks in: if it's a "pure" host (i.e. with no path) then the client gets redirected to the host you specified, with the original request path appended. If your url does contain a path, then the request path is ignored ๐
Examples: the following swaps the first two path components of the original URL:
Service
Host -re "(.+)"
URL "^/([^/]+)/([^/]+)(/.*)?"
Redirect "http://$0(1)/$2/$1$3"
End
Notice the use of Host to supply the hostname part for the redirect ๐
Using request accessors, the above example can be rewritten as:
Service
URL "^/([^/]+)/([^/]+)(/.*)?"
Redirect "http://%[header host]/$2/$1$3"
End
Compatibility syntax: if you specified
Redirect "http://abc.example"
and the client requested http://xyz/a/b/c then it will be redirected to http://abc.example/a/b/c, but if you specified
Redirect "http://abc.example/index.html"
it will be sent to http://abc.example/index.html.
๐๏ธ SendFile DIR
The static file backend, and it's iconic tbh. It responds with the content of a file from directory DIR. The path part of the request supplies the path to the requested file, relative to the directory DIR. That path may be modified โ if need be โ using request rewriting techniques (in particular SetPath) discussed in REQUEST MODIFICATION DIRECTIVES.
Content type of the response is assumed to be text/plain. You can change that using the SetHeader statement (see RESPONSE MODIFICATION) ๐
โ Success
A special backend that responds with code 200. Use it for debugging or for side effects, like setting headers with the SetHeader statement. Main character energy fr fr ๐
๐ง Session control
๐ธ Session
Directives enclosed between Session and the following End directives define a session-tracking mechanism for the current service. See below for details ๐
๐ฆ Other directives
๐ Constant "NAME" [OPTIONS] ["VALUE"]
Defines a configuration constant for use in string expansions and Lua code. See Constant in section GLOBAL DIRECTIVES for a detailed discussion of its arguments and their meaning.
Per-service constants augment per-listener and global ones. If the name of a per-service constant coincides with the name of a per-listener or globally defined one, the former overrides the latter for this service. Periodt โจ
๐ IgnoreCase bool
Override the global IgnoreCase setting.
โ ๏ธ Heads up bestie: this statement is deprecated and will be removed in future versions. Please use the-icaseoption to theURLdirective instead. See the discussion of options in Service Matching Directives section above.
๐ค Disabled bool
Start pound with this service disabled (true) or enabled (false). If started as disabled, the service can be later enabled with poundctl(8). Cute little on/off switch, love that for you ๐
๐จ ForwardedHeader name
Defines the name of the HTTP header that carries the list of proxies the request has passed through. It's used to report the originator IP address when logging โ see the description of the %a specifier in REQUEST LOGGING. This statement overrides the ForwardedHeader directive from the listener scope.
The default is X-Forwarded-For.
๐ TrustedIP
Defines a list of trusted proxy IP addresses, used to determine the originator IP. See the description of the %a specifier in REQUEST LOGGING for a detailed discussion.
This statement overrides the TrustedIP directive from the listener scope.
๐คซ LogSuppress class
Suppresses HTTP logging for requests that resulted in status codes from the specified class. The valid status classes are:
infoor1โ 1xx response codes.successor2โ 2xx response codes.redirector3โ 3xx response codes.clterror4โ 4xx response codes.srverror5โ 5xx response codes.allโ all response codes.
Multiple arguments are allowed ๐ฏ
This statement is designed for services that receive a constant stream of similar HTTP requests from a controlled set of IP addresses, such as e.g. Openmetric services. See the Metric section below for an example ๐
๐ ACME
This statement creates a service specially crafted for answering ACME HTTP-01 challenge requests (see https://letsencrypt.org/docs/challenge-types/#http-01-challenge). It takes a single argument specifying a directory where ACME challenges are stored. That argument is subject to string expansion (see the subsection String expansion, above) โจ
It's supposed that another program is started periodically, which checks for certificates approaching their expiration, issues renewal requests and stores the obtained ACME challenges in that directory. Teamwork makes the dream work ๐ซถ
๐ Backend ๐
okay so a backend is literally just the definition of one single backend server that pound uses to reply to your incoming requests. that's it. that's the whole vibe. backends get defined with a Backend statement, and there are two flavors: service-specific and global (aka "named"). we'll get into both later in this section, don't worry bestie. everything you put between Backend and End applies to that one backend only.
here's what you can put in there:
Addressaddress โ the address pound connects to. this can be a numeric IP address, a symbolic host name (which has to be resolvable at run-time, no pressure or anything), or a full pathname of a UNIX socket. if the name can't be resolved to a valid address, pound will assume it's the path to a Unix-domain socket. this one is mandatory โ no Address, no backend, periodt.Portport โ the port number or service name pound connects to. mandatory for non Unix-domain backends. (if it is a Unix socket, you get a pass ๐ )HTTPSโ the backend is using HTTPS. that's it. that's the whole directive. slay.ServerName "name"โ sets the name to use for server name identification (SNI). this directive also rewrites theHost:header for this particular backend, so you don't have to also useSetHeaderon top of it. one less thing, love that for you. โ ๏ธ may only appear after theHTTPSdirective.Cert "filename"โ the certificate pound uses as a client. the filename is the file containing the certificate โ possibly a certificate chain and the signature too. also only allowed afterHTTPS.Disable SSLv2|SSLv3|TLSv1|TLSv1_1|TLSv1_2โ disables the protocol and all lower protocols as well. why? because of a limitation in OpenSSL, which does not support disabling a single protocol. so like,Disable TLSv1would disable SSLv2, SSLv3 and TLSv1, thus allowing only TLSv1_1 and TLSv1_2. ๐ only afterHTTPS, bestie.Ciphers [typeopt] "ciphers" ...โ defines the list of ciphers (for TLSv2 and below) or ciphersuites (for TLSv3) to negotiate with the backend. the typeopt option defines how to interpret the ciphers argument that follows it:-cipherlistโ sets the list of ciphers that affects only TLSv2 and prior protocols. this is the default.-ciphersuitesโ sets the list of TLSv3 ciphersuites. it gets combined with any TLSv1.2 and below cipher lists that may have been configured.
refer to ciphers(1) for a whole discussion of the ciphers argument. and yeah โ only after HTTPS. ๐ซถ
Priorityn โ the priority of this backend. higher priority backends get used more often than lower priority ones, so you should give higher priorities to your more capable servers. the scheduling algorithm depends on the load balancing strategy in use (peep theREQUEST BALANCINGsection above ๐). allowed range for priorities is0to65535, inclusive. default value is5.TimeOutn โ override the globalTimeOutvalue.ConnTOn โ override the globalConnTOvalue.WSTimeOutn โ override the globalWSTimeOutvalue.Disabledbool โ start pound with this backend disabled (1) or enabled (0). if it starts disabled, you can enable it later withpoundctl(8). iconic.
โจ Dynamic backends โจ
okay this is the fun part โ dynamic backends get created and updated on the fly based on info from DNS. ๐ฆ a backend is declared dynamic by the Resolve statement, and its value defines what type of DNS record is used to generate the dynamic backend instances:
firstโ resolve the symbolic host name from theAddressdirective and use the first IP from the DNS response as the address of the created dynamic backend. thus, at most one dynamic backend will be produced.allโ resolve the symbolic host name from theAddressdirective and create one backend for each address from the DNS response. this enables load balancing between the created backends. each backend gets assigned the same priority. ๐srvโ obtain SRV records for the host name and use them to generate backends. each record produces a new dynamic backend of theResolve alltype, which produces regular backends as described above. the weight field of the SRV record is mapped to the priority field of each generated backend. the SRV priority field identifies the balancer group where the backend will be hosted.
and these directives let you configure your dynamic backends even further:
Family any | inet | inet6โ selects the family of addresses to look for:anyโ use all address families available. this is the default.inetโ use only IPv4 addresses (A DNS RRs).inet6โ use only IPv6 addresses (AAAA DNS RRs).
IgnoreSRVWeightbool โ when using SRV records, ignore their weight fields and assign priorities for the generated backends from the priority of the producing backend. only valid when used together withResolve srv. ๐OverrideTTLN โ query DNS each N seconds for possible changes in the configuration of the existing dynamic backends. if you don't use this statement, the TTL value returned with the DNS response gets used instead.RetryIntervalN โ retry failed DNS queries each N seconds. default is600.
๐ธ Global and per-service backends ๐ธ
a Backend definition can appear as part of a service (i.e. inside a Service section) or globally. for the global one, the Backend keyword has to be followed by a symbolic name you assign to that backend, like:
Backend "name"
...
End
this name has to uniquely identify the backend among the other globally defined backends, no copy-paste name drama please. a global backend gets attached to a service using the UseBackend statement, which takes the symbolic name of the backend as its argument:
UseBackend "name"
this has essentially the same effect as if the full backend definition appeared in that spot instead. the substantial difference between service-specific and global backends is that a single global backend can be used in multiple services. in a way, UseBackend expands to a backend definition within the service. ๐ซถ
and bestie, you can also adjust the Priority and Disabled parameters of the global backend definition for use in a particular service. the syntax goes like this:
Backend "name"
Priority 3
Disabled no
End
in this form, the Backend statement acts as UseBackend. only two directives can appear within such a Backend statement: Priority and Disabled. they override the settings defined in the global backend "name".
โ ๏ธ notice that โ in spite of their similarity โ the two named forms of theBackendstatement serve different purposes: in global scope, a namedBackenddefines a backend for further use in various services, and within a service, it creates a copy of the backend defined elsewhere and attaches it to this service. same look, different job. iykyk.
the actual global definition may appear before as well as after the service or services it's used in. no strict order, we're chill like that. ๐
๐ Resolver ๐
the Resolver section controls DNS lookups for dynamic backend generation (see above, bestie, keep up ๐). it can contain these directives:
CNAMEChainN โ maximum allowed length of a CNAME chain. CNAME chains are formed by DNS CNAME records pointing to another CNAME. although it's prohibited by the RFC, this kind of usage does occur sometimes (rude). by default, pound does not accept CNAME chains. if you work with nameservers that return them, set this statement to a small integer value defining the max number of CNAMEs in the chain that can be accepted. a value of2or3should suffice in most cases.ConfigFile "FILE"โ read the resolver configuration from FILE, instead of the default/etc/resolv.conf.ConfigText ... Endโ alternative way to supply resolver configuration. the material within this section is read verbatim and used as the content of the resolver configuration file. if bothConfigFileandConfigTextare used, the last used statement wins.debugbool โ whether to enable DNS debugging info.RetryIntervalN โ interval in seconds, after which to retry failed DNS queries or queries that returned no RRs. this value is used unless the backend defines its own retry interval value.
๐ฅ Emergency ๐ฅ
emergency servers get used once all existing backends are "dead". ๐ everything between Emergency and End is specific to a single service. for the list of directives allowed in Emergency sections, refer to the description of Backend โ same rules apply, bestie.
๐ Session ๐
this one defines how a service deals with possible HTTP sessions. all configuration directives between Session and End are specific to a single service. once a session is identified, pound will try to send all requests within that session to the same backend server. consistency is the moment. โจ
here's what's available:
Type IP|BASIC|URL|PARM|COOKIE|HEADERโ what kind of sessions are we even looking for?IP(the client address),BASIC(basic authentication),URL(a request parameter),PARM(a URI parameter),COOKIE(a certain cookie), orHEADER(a certain request header). this is a mandatory parameter, no skipping.TTLn โ how long can a session be idle (in seconds)? a session that's been idle for longer than the specified number of seconds gets discarded. also mandatory. ๐ID "name"โ the session identifier. this directive is permitted only for sessions of typeURL(the name of the request parameter we need to track),COOKIE(the name of the cookie), andHEADER(the header name).
see below for some examples. ๐
๐ Metrics ๐
the following service definition enables Openmetric telemetry output on endpoint /metrics:
Service
URL "/metrics"
Metrics
End
to control access to the telemetry endpoint, use the ACL statement.
the LogSuppress directive can be used in openmetric services to suppress logging of served HTTP requests.
for example:
Service
URL "/metrics"
Metrics
ACL "secure"
LogSuppress success
End
the metrics output is sufficiently self-documented by # HELP descriptor lines. so like, it explains itself, which is honestly main character energy. ๐
HIGH-AVAILABILITY ๐
okay so pound is lowkey keeping tabs on your active backend servers at all times. It attempts to keep track of active backend servers, and will temporarily disable servers that do not respond โ and bestie, โdo not respondโ does not necessarily mean dead: an overloaded server that pound cannot establish a connection to will be considered dead. ๐ญ
But every Alive seconds, pound makes an attempt to connect to the dead servers in case they have become active again. If this attempt succeeds, connections will be initiated to them again. ๐ซถ
In general it is a good idea to set this time interval as low as is consistent with your resources in order to benefit from resurrected servers at the earliest possible time. The default value of 30 seconds is probably a good choice. โจ
The clients that happen to hit a dead backend server will just receive a 503 Service Unavailable message. ๐
REQUEST MODIFICATION DIRECTIVES ๐
Several statements are provided to modify incoming requests prior to passing them to the backend. These statements can be present both in ListenHTTP (ListenHTTPS) and in Service sections. ๐
Basic request modification directives are:
SetURL "value"โ Set the URL of the incoming request to value.SetPath [-encode] "value"โ Set the path part of the URL to the given string. The-encodeoption enables percent-encoding of value.SetQuery "value"โ Set the query part of the URL to the given string. value must be a valid query with the special characters properly encoded using percent encoding.SetQueryParam "name" [-encode] "value"โ Set the query parameter name to the value. If-encodeis given, after usual expansions value will undergo percent-encoding.SetHeader "name: value"โ Sets the HTTP header. If the header name already exists, it will be overwritten. Otherwise, new header will be added to the end of the header list.DeleteHeader [options] "pattern"โ Remove from the request all headers matching pattern. By default, pattern is treated as extended POSIX regular expression. The options argument can be used to change this behavior. It consists of zero or more option flags from the following list:-begโ Exact match at the beginning of string (prefix match).-caseโ Case-sensitive comparison.-containโ Match substring.-endโ Exact match at the end of string (suffix match).-exactโ Use exact string matching.-icaseโ Case-insensitive comparison.-pcreor-perlโ Use Perl-compatible regular expression (requires compilation-time support). This overrides globalRegexTypesettings.-posixโ Use POSIX extended regular expression. This overrides globalRegexTypesettings.-reโ Use regular expression matching, as set by theRegexTypestatement.
The value argument in the above directives is subject to string expansion. Refer to subsection String expansions, for details. ๐ฆ
The Rewrite block statement is used to associate one or more of the above directives with request matching directives (discussed in the subsection Service Matching Directives above), so that request modification takes place only when the request matches certain conditions. ๐
Syntactically, a Rewrite block is:
Rewrite [request]
.
.
.
Else
.
.
.
End
where dots stand for any number of request matching and request modification statements. The Else part is optional; any number of Else blocks can be supplied, thus providing for conditional branching. The following concocted example illustrates it:
Rewrite
Path "\.(jpg|gif)$"
SetPath "/images%[path]"
Else
Match AND
Host "example.org"
Path "\.[^.]+$"
End
SetPath "/static%[path]"
Else
Path "\.[^.]+$"
SetPath "/assets%[path]"
End
Here, if a path ends with a filename with suffix ".jpg" or ".gif", "/images" is prepended to it (notice that path always starts with a slash, hence it should not be used explicitly). If the path ends with any other extension, the action depends on the host being addressed. The path is prefixed with "/static" if the host is "example.org", and with "/assets" otherwise. ๐ธ
To illustrate the effects of string expansion, hereโs the above example rewritten using backreference expansion instead of request accessors:
Rewrite
Path ".*\.(jpg|gif)$"
SetPath "/images$0"
Else
Match AND
Host "example.org"
Path ".*\.[^.]+$"
End
SetPath "/static$0"
Else
Path ".*\.[^.]+$"
SetPath "/assets$0"
End
Request modification directives from ListenHTTP (or ListenHTTPS) section are applied first, followed by directives from the Service section. Directives appearing within a single section are applied in the same order as they appear in the configuration file. โจ
BUILT-IN HEADERS ๐
In addition to the SetHeader and DeleteHeader directives discussed above, the HeaderOption directive controls insertion of the built-in headers:
HeaderOption opt...
There are two kinds of such headers: forwarded headers that convey information about original destination of the request, and ssl headers (for HTTPS connections), that hold information about server and client SSL certificates. These are discussed in detail below. ๐
By default, both kinds of built-in additional headers are enabled. This default can be changed by using the HeaderOption directive. Placed in global scope, this directive sets global options. Used within a ListenHTTP or ListenHTTPS block, it affects only settings for that listener. ๐ซถ
The opt values passed to the directive are:
allโ Enable both kinds of additional headers.noneโ Disable both kinds of additional headers.forwardedโ Enable forwarded headers.no-forwardedโ Disable forwarded headers.sslโ Enable ssl headers.no-sslโ Disable ssl headers.
The built-in headers are added before request modification directives are applied. Thus, you can use DeleteHeader and SetHeader to trim down headers added by HeaderOptions. ๐
Forwarded Headers ๐
The headers in the forwarded header group are:
X-Forwarded-Forโ IP address of the HTTP client that sent the request.X-Forwarded-Protoโ Protocol (httporhttps) that the client used to connect to pound.X-Forwarded-Portโ Destination port that the client used to connect to pound.
HTTPS Headers ๐ฅ
If a client browser connects to pound via HTTPS and the ssl header group is enabled, then the following header is added:
X-SSL-Cipherโ Contains SSL version followed by a slash and active cipher algorithm.
Additionally, if the client presented its certificate, the following headers are added that describe the client certificate:
X-SSL-Subjectโ Details about the certificate owner.X-SSL-Issuerโ Details about the certificate issuer (Certificate Authority).X-SSL-NotBeforeโ Starting date of certificate validity.X-SSL-NotAfterโ Ending date of certificate validity.X-SSL-Serialโ Certificate serial number (decimal).X-SSL-Certificateโ The full client certificate (PEM-format multi-line).
RESPONSE MODIFICATION ๐
Headers in the response obtained from a regular backend or generated with an Error backend can be modified before sending them back to the requesting server. A special form of the Rewrite directive is provided for this purpose:
Rewrite response
.
.
.
Else
.
.
.
End
where dots stand for any number of response matching and modification statements. The Else part is optional; any number of Else blocks can be supplied, thus providing for conditional branching. ๐ฆ
This directive can be used in ListenHTTP, ListenHTTPS, and Service definitions. ๐
Response modification directives from the Service section are applied first, followed by directives from the ListenHTTP (or ListenHTTPS) section. Directives appearing within a single section are applied in the same order as they appear in the configuration file. โจ
Response matching statements are:
Header [options] "pattern"โ Matches if the response contains at least one header matching the given pattern.Header "name" [options] "pattern"โ Alternative form of theHeaderstatement, that matches pattern against value part of each header name.StringMatch "string" [options] "pattern"โ Expand string as described inString expansion(see below) and match the resulting value against pattern. By default, pattern is treated as case-insensitive extended regular expression. This can be changed by options, as described in sectionService Matching Directives, above.Matchโ TheMatchstatement is as described in sectionMatch statement, except that only directives described above are allowed within it.Notโ Placed before any of the above directives, reverts its result.
Following response modification statements are defined:
DeleteHeader "pattern"โ Remove matching headers from the response.SetHeader "header: value"โ Add header to the response. Argument undergoes string expansion as described above. The expanded value must be a valid header line.
ERROR RESPONSE REWRITING ๐
When a regular backend responds with an error, the content of that response can be replaced with an arbitrary custom page, if its status code is one of the http status codes supported by pound, i.e. one of 400, 401, 404, 405, 413, 414, 500, 501, 503, and a custom error page for that status is provided using the ErrorFile directive (or one of the legacy ErrNNN directives). ๐ซถ
To enable such error response rewriting use the following statement:
RewriteErrors on
in the Service block, where the responding backend is defined, or in the enclosing ListenHTTP (ListenHTTPS) block. ๐
๐ MULTI-VALUE HEADERS ๐
Okay bestie, so HTTP is lowkey dramatic and lets certain headers show up multiple times in one message. Specifically, multiple headers with the same header name are allowed if that header field is defined as a comma-separated list. The standard says those fields can be combined into a single header: value pair by appending each subsequent field value to the previous one, each separated by a comma. Slay.
Pound can do that combining on incoming requests and on responses. To turn it on, declare the names of headers that can be combined using the CombineHeaders section statement. This statement must be followed by header names, each on a separate line, and terminated with an End statement. E.g.:
CombineHeaders
"Accept"
"Allow"
"Forwarded"
End
You can also store header names in a file and include that file after CombineHeaders, e.g.:
CombineHeaders
Include "multivalue.txt"
End
๐ BASIC AUTHENTICATION ๐
To limit access to a service or services to certain authenticated users, use a combination of BasicAuth matching and response modification. It's giving security gatekeeper energy.
The BasicAuth matching directive evaluates to true if the incoming request contains an Authorization header with scheme "Basic", such that the user name and password obtained from it match a line in the given disk file. The file must be a plain-text file created with htpasswd(1) or a similar utility, i.e. each non-empty line of it must contain a username and password hash separated by a colon. The password hash can be one of:
- Password in plain text.
- Hash created by the system
crypt(3)function. - Password hashed using SHA1 algorithm and encoded in base64. This hash must be prefixed by
{SHA}. - Apache-style
apr1hash.
Combined with the response rewriting described above, this can be used to implement basic HTTP authentication in Pound as shown in the following example:
Service "auth"
Not BasicAuth "/etc/pound/htpass"
Rewrite response
SetHeader "WWW-Authenticate: Basic realm=\"Restricted access\""
End
Error 401
End
Placing this service definition before others ensures that services defined below it will be tried only if the incoming request passes basic authentication. Iconic ordering behavior, honestly.
๐ฆ REQUEST RATE ๐ฆ
Request rate can be controlled using the TBF service selection statement. The statement implements a token bucket filter and takes three arguments: a string identifying the bucket, request flow rate in requests per second, and token burst size, i.e. maximum number of requests per bucket. The first argument is expanded as discussed in String expansion. The statement evaluates to true if the request conforms to the rate policy, and to false otherwise. It is customary to use its negation together with the Error special backend in order to control request flow, e.g.:
Service "rate-limit"
Path -re "^/([^/]+).*"
Not TBF "$1" 3 10
Error 429
End
๐ REQUEST LOGGING ๐
Logging of HTTP requests and responses is controlled by the LogLevel directive. This directive can appear either in global scope, in which case it affects all listeners defined below it, or in listener scope, in which case it affects only this listener, overriding the global setting. Very main-character-of-the-listener behavior.
Argument to the LogLevel directive can be either an integer number in range 0 through 5 (inclusive), or a quoted string. Numeric value identifies one of the built-in log formats, described later in this section. String value refers either to a built-in format name, or to a user-defined format name. In the latter case, the named format must be defined earlier using the LogFormat statement.
โจ Format specification โจ
The syntax of the LogFormat statement is:
LogFormat "name" "format_string"
First argument, name, is a unique identifier for this format, which will be used later to refer to it. Format_string is a character string composed of ordinary characters (not %), which are copied unchanged to the resulting log message, and conversion specifications, each of which are replaced by a corresponding piece of information about the request or reply.
Conversion specifications are single characters prefixed with a percent sign. Depending on the specification, an optional conversion argument in curly brackets may appear between % and the conversion character.
The following conversion characters are defined:
%%โ Replaced with the percent sign.%aโ Originator IP address of the request. If the request contains theX-Forwarded-Forheader andTrustedIPACL is defined, the value of the header is consulted to obtain the IP address. The value must be a comma-delimited list of intermediate user agent IP addresses. To determine the actual user agent IP, the list is traversed from right to left, until an IP is found that is not listed inTrustedIPACL.
If X-Forwarded-For header is not present, or TrustedIP is not defined, or the above algorithm does not return an IP address, %a expands to the actual remote IP address the request came from (same as %h).
The TrustedIP ACL can be defined in global scope, or in ListenHTTP (ListenHTTPS) section, or in Service section. Most-specific ACL overrides least-specific ones, that is TrustedIP defined in Service will be used, if it is defined. If not, the one defined in listener will be used, etc. The syntax of the TrustedIP statement is the same as that of ACL, i.e.
TrustedIP "name"
refers to the named ACL name (which must be defined earlier),
TrustedIP -file "name"
or
TrustedIP -filewatch "name"
read the list of IPs (or CIDRs) from the file name, and
TrustedIP
"CIDR0"
"CIDR1"
...
End
defines the list of trusted IPs in place.
ForwardedHeader statement defines the name of the header to use instead of X-Forwarded-For. As TrustedIP, this statement can appear in global, listener, or in service scope.
%Aโ Local IP address of the listener.%Bโ Size of response in bytes, excluding headers.%bโ Same as%B, but in CLF format, i.e. a dash is used when response size is zero.%Dโ The time taken to serve the request, in microseconds.%hโ Client IP address of the request.%Hโ The request protocol.%{VARNAME}iโ The contents of VARNAME: header line in the request. Changes made by header modification directives affect this.%{VARNAME}Iโ Same as%i, except that if no such header is present in the request, a dash is substituted.%lโ Single dash. This specifier is reserved for future use.%{OBJNAME}Lโ Location of the Pound object that is involved in handling the request. Valid values for OBJNAME are:listener,service, andbackend. The location gives position in the configuration file where the object was defined, and is formatted as
NAME:LN1.COL1-LN2.COL2
where NAME is the configuration file name, LN1 and COL1 are number of line and column where the object definition begins, and LN2 and COL2 are number of line and column where it ends. Line and column numbers start with 1.
%mโ The request method.%{OBJNAME}Nโ Name of the Pound object that is involved in handling the request. Valid values for OBJNAME are:listener,service, andbackend.%pโ Port number of the listener serving the request.%Pโ Thread ID of the serving thread.%qโ The query string prepended with a?if it exists, otherwise an empty string.%rโ First line of request.%sโ Response status code.%>sโ First line of the response.%tโ Time the request was received, in the format[18/Sep/2011:19:18:28 -0400]. The last number indicates the timezone offset from UTC.%{format}tโ Time the request was received, in the format specified by the argument (strftime(3)). If the format starts withbegin:(default) the time is taken at the beginning of the request processing. If it starts withend:it is the time after the response from the backend has been sent back to the requester. In addition tostrftime(3)formats, the following specifications are recognized:secโ Number of seconds since the Epoch.msecโ Number of milliseconds since the Epoch.usecโ Number of microseconds since the Epoch.msec_fracโ Millisecond fraction of the time.usec_fracโ Microsecond fraction of the time.
%Tโ The time taken to process the request, in seconds.%{UNIT}Tโ The time taken to process the request, in a time unit given by UNIT. Valid units aremsfor milliseconds,usfor microseconds,sfor seconds, andffor seconds with fractional part. Usingsgives the same result as%Twithout any format; usingusgives the same result as%D.%uโ Remote user if the request was authenticated.%Uโ The URL path requested. This is affected by request modification directives.%vโ Hostname used in the request (from theHostheader, without port number).
๐ Built-in formats ๐
There are five built-in formats. These can be identified either by their numeric index in the format table, or by symbolic name. The table below describes each built-in format using the format specification string:
0,nullโ No request logging at all.1,regularโ
"%a %r - %>s"
2,extendedโ
"%a %r - %>s (%{Host}i/%{service}N -> %{backend}N) %{f}T sec"
3,vhost_combinedโ (Split in two lines for readability)
"%v:%p %a %l %u %t \"%r\" %s %b \"%{Referer}i\" \
\"%{User-Agent}i\""
4,combinedโ
"%a %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\""
5,detailedโ (Split in two lines for readability)
"%v:%p %a %l %u %t \"%r\" %s %b \"%{Referer}i\" \
\"%{User-Agent}\" (%{service}N -> %{backend}N) %{f}T sec"
๐ก๏ธ SECURITY ๐ก๏ธ
okay so bestie, pound is super low-key about your hard disk. The config file, server certificate files and error messages (ErrorFile) are opened read-only on startup, read, and then closed immediately afterwards. No lingering, no weird disk drama.
Read access during runtime is only required for certain special features, like BasicAuth, SendFile, and request matching directives with the -filewatch option. And the only disk write pound ever does is saving its PID in the pid file at startup. That's it. That's the whole disk footprint. ๐
Giving such limited I/O, using a RootJail directive can provide only limited security bonus. Cute, but not a magic shield, iykyk.
pound tries to sanitize all HTTP/HTTPS requests: the request itself, the headers and the contents are checked for conformance to the RFCs, and only valid requests are passed to the backend servers. This is not absolutely fool-proof โ as the recent Apache problem with chunked transfers demonstrated. However, given the current standards, this is the best that can be done โ HTTP is an inherently weak protocol. fr fr. ๐ญ
๐ DEPRECATED FEATURES ๐
Configuration statements
The following configuration statements are retained for backward compatibility with earlier pound versions. They will disappear from future releases โ so don't get too attached, bestie:
HeadRequireโ useHeaderinstead. See Service Matching Directives, for details.HeadDenyโ useNot Header. For details, see the Negation subsection in Service Matching Directives.AddHeaderandHeaderAddโ useSetHeaderinstead. See the section REQUEST MODIFICATION DIRECTIVES, for details.HeaderRemoveandHeadRemoveโ useDeleteHeaderinstead. See the section REQUEST MODIFICATION DIRECTIVES, for details.IgnoreCaseโ use the-icaseoption to theURLdirective instead. See the discussion of options in the Service Matching Directives section.ErrNNN(whereNNNis one of:400,401,403,404,413,414,500,501,503) โ use theErrorFiledirective, described in the section HTTP Listener, above.
Redirect request hack
The use of the redirect request hack in the Redirect statement is deprecated as well. Instead of relying on the trailing slash to append the original URL to the redirect location, use URL backreferences. For example, instead of
Redirect "http://example.org/"
use:
URL ".*"
Redirect "http://example.org$0"
Multiple redirect backends
Use of multiple Redirect backends in a single service is deprecated, as well as using Redirect backends together with regular ones. If you know of any valid usage for such configuration, please drop a note to the authors (see REPORTING BUGS, below). ๐ซถ
๐ ADDITIONAL NOTES ๐
pound uses the system log for messages (default facility LOG_DAEMON). If using rsyslog, you can use the following configuration fragment to redirect pound messages to a separate file:
:programname, startswith, "pound" {
/var/log/pound.log
stop
}
The format requested by LogLevel 3 is understood by most log analyzers. iconic.
Translating HTTPS to HTTP is an iffy proposition: no client information is passed to the server itself (certificates, etc) and the backend server may be misled if it uses absolute URLs. pound tries to handle this by adding various X-Forwarded-* and X-SSL-* headers. See HEADER MODIFICATION above, for a detailed discussion.
pound deals with (and sanitizes) HTTP/1.1 requests. Thus even if you have an HTTP/1.0 server, a single connection to an HTTP/1.1 client is kept, while the connection to the backend server is re-opened as necessary. main character energy: keeps the client connection alive, reopens the backend like it's no big deal. โจ
pound attempts to resolve the names of the hosts that appear in various requests and/or responses. That means it needs a functioning resolver of some kind (be it /etc/hosts, DNS or something else). However, the use of DNS can be disabled using the -W no-dns command line option.
๐ EXAMPLES ๐
To translate HTTPS requests to a local HTTP server (assuming your network address is 192.0.2.1):
ListenHTTPS
Address 192.0.2.1
Port 443
Cert "/etc/pound/server.pem"
Service
Backend
Address 127.0.0.1
Port 80
End
End
End
To distribute the HTTP/HTTPS requests to three Web servers, where the third one is a newer and faster machine:
ListenHTTP
Address 192.0.2.1
Port 80
End
ListenHTTPS
Address 192.0.2.1
Port 443
Cert "/etc/pound/server.pem"
End
Service
Backend
Address 192.168.0.10
Port 80
End
Backend
Address 192.168.0.11
Port 80
End
Backend
Address 192.168.0.12
Port 80
Priority 3
End
End
To separate between image requests and other Web content and send all requests for a specific URL to a secure server:
ListenHTTP
Address 192.0.2.1
Port 80
End
# Images server(s)
Service
URL ".*.(jpg|gif)"
Backend
Address 192.168.0.12
Port 80
End
End
# redirect all requests for /forbidden
Service
Url -beg "/forbidden"
Redirect "https://xyzzy.com"
End
# Catch-all server(s)
Service
Backend
Address 192.168.0.10
Port 80
End
Backend
Address 192.168.0.11
Port 80
End
Session
Type BASIC
TTL 300
End
End
Here is a more complex example: assume your static images (GIF/JPEG) are to be served from a single backend 192.168.0.10. In addition, 192.168.0.11 is to do the hosting for www.myserver.com with URL-based sessions. Two backends, 192.168.0.20 and 192.168.0.21, will handle the rest of requests (cookie-based sessions). Among these, the latter (192.168.0.21) is less powerful than the former, so care should be taken that it gets less requests. The logging will be done by backend servers. All plain HTTP requests are to be redirected to HTTPS and all requests directed to myserver.com โ to www.myserver.com. The configuration file may look like this:
User "nobody"
Group "nogroup"
RootJail "/var/pound/jail"
Alive 60
LogLevel 0
# HTTP: redirect all requests to the corresponding HTTPS server.
ListenHTTP
Address 192.0.2.1
Port 80
Client 10
Service
Host -re ".+"
URL ".*"
Redirect 301 "https://$0(1)$0"
End
End
ListenHTTPS
Address 192.0.2.1
Port 443
Cert "/etc/pound/pound.pem"
Client 20
End
# Image server
Service
URL ".*.(jpg|gif)"
Backend
Address 192.168.0.10
Port 80
End
End
# Virtual host www.myserver.com
Service
URL ".*sessid=.*"
Host "www.myserver.com"
Backend
Address 192.168.0.11
Port 80
End
Session
Type URL
ID "sessid"
TTL 120
End
End
# Virtual host myserver.com - redirects to www.
Service
Host "myserver.com"
URL ".*"
Redirect 301 "https://www.myserver.com$0"
End
# Virtual host server1.com and www.server1.com
Service
Host -re "^(www.)?server1.com$"
Backend
Address 192.168.0.20
Port 80
Priority 5
End
Backend
Address 192.168.0.21
Port 80
Priority 4
End
Session
Type COOKIE
ID "userid"
TTL 180
End
End
# Return custom error page if no matching service was found.
Service
Error 404 "/var/lib/pound/notfound.html"
End
๐ FILES ๐
๐พ /var/run/pound.pid
This is where pound will attempt to record its process id. The exact location is determined at compile time by the value of the --localstatedir configuration switch. It can be changed at runtime using the -p command line option. Use pound -V to inspect the actual default.
๐พ /etc/pound.cfg
Default configuration file. The exact location is determined at compile time by the value of the --sysconfdir configuration switch. It can be changed at runtime using the -f command line option. Use pound -V to inspect the actual default.
๐พ /usr/local/etc/pound/cert.pem
Certificate file(s) for HTTPS. The location must be defined in the configuration file โ this is only a suggestion. The file must contain a PEM-encoded certificate, optionally a certificate chain from a known Certificate Authority to your server certificate and a PEM-encoded private key (not password protected). See openssl(1) for details. This file should be well protected, lest someone gets your server private key. ๐
๐ AUTHOR ๐
Written by Robert Segall (Apsis GmbH), and Sergey Poznyakoff.
๐ REPORTING BUGS ๐
Report bugs to gray@gnu.org. You may also use github issue tracker at https://github.com/graygnuorg/pound/issues.
ยฉ๏ธ COPYRIGHT ยฉ๏ธ
Copyright ยฉ 2002-2010 Apsis GmbH.Copyright ยฉ 2018โ2026 Sergey Poznyakoff
License GPLv3+: GNU GPL version 3 or later (http://gnu.org/licenses/gpl.html)
This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
okay bestie, that's the whole POUND(8) page โ source/version pound, dated May 7, 2026. stay iconic out there ๐
โจ๐ฆ
๐ฆ where this page comes from
- alpine 3.24: pound-doc 4.23-r0 ยท GPL-3.0-or-later
- Copyright (C) 2002-2010 Apsis GmbH
- Copyright (C) 2018-2026 Sergey Poznyakoff
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms