- Table of contents
- Performance Tuning
- important performance tuning rules
- lighttpd configuration performance tuning (technical guidelines)
- lighttpd configuration for use of operating system (OS) features
- lighttpd configuration tuning for high-traffic sites with a large number of connections
- lighttpd configuration tuning for low-memory systems
- lighttpd configuration tuning for lighttpd behind a reverse proxy
- lighttpd configuration tuning for traffic shapping (download rate-limiting)
- lighttpd configuration tuning for timeouts
- lighttpd configuration tuning for benchmarking
- Platform-Specific Notes
Performance Tuning¶
Resource Tuning is recommended before diving into the details below.
important performance tuning rules¶
- Prefer lighttpd defaults unless you have a reason to change a setting, and unless you test that changing the setting is beneficial to you.
- Proper functionality is more important than marginal increases in performance; a web server that does not function as intended is not useful.
Do not sacrifice security or desired operational functioning for marginal performance improvements. - Performance tuning is not magic. The recommended approach is that one change be made at a time, that the change be tested and benchmarked, and that if the change does not have a measurable and positive impact in real-world scenarios, that the change be reverted.
lighttpd is generally pretty snappy. Most of the following are micro-optimizations. No changes are required unless you have a specific performance issue that you must address.
(Please refer to configuration options for details about any of the options listed below.)
lighttpd configuration performance tuning (technical guidelines)¶
- less is more (and is often simpler, too)
- rely on defaults where possible to reduce unnecessary (duplicative) config processing (at runtime) to process configuration directives which were already set to the default values
- set config options in the global scope rather than repeating in sub-scopes. lighttpd optimizes configuration settings in the global scope and makes those settings the defaults
- TLS configuration can be set in the global scope and inherited by multiple
$SERVER["socket"]
ssl.pemfile = "..."
ssl.privkey = "..."
$SERVER["socket"] == ":443" { ssl.engine = "enable" }
$SERVER["socket"] == "[::]:443" { ssl.engine = "enable" }
- list only the modules actually used and enabled in
server.modules
; comment out the others
- each loaded module registers itself into lighttpd hooks and gets a chance to handle each request, which is is unnecessary if a module is loaded but not otherwise configured to be used
-server.compat-module-load = "disable"
skips loading the default modules (mod_indexfile, mod_dirlisting, mod_staticfile), and you can then explicitly add one or more toserver.modules
to use them
(lighttpd 1.4.66 and later avoid loading mod_indexfile and mod_dirlisting if not configured and activated, soserver.compat-module-load = "disable"
no longer adds much value.) - tweaks to remove optional functionality
-server.tag = ""
skips sending "Server: lighttpd/1.4.xx" in responses; alternatively, use:server.tag = "lighttpd"
to hide the lighttpd version
-server.range-requests = "disable"
can be used if all server responses are small files, but otherwise it is recommended to be left enabled - review the default lighttpd config provided by your distro
- configs provided by distros aim to be newbie friendly but can introduce complexity of yet another config framework
- configs provided by distros are often out-dated and then kept for historic compatibility, rather than current best practices
- example: ~20 years ago some widely used versions of Adobe Acrobat reader plugin PDF clients misbehaved with range requests.
Unfortunately, the config setting to disable range requests for PDFs has been cargo-culted into configs since then.
Prefer to comment out or remove:$HTTP["url"] =~ "\.pdf$" { server.range-requests = "disable" } # remove from lighttpd config
Range requests are used by Adobe PDF Fast Web View, which is recommended by Adobe for large PDFs.
See "Optimize The PDF For Fast Web View" section at the bottom of https://helpx.adobe.com/ie/acrobat/using/optimizing-pdfs-acrobat-pro.html server.max-connections
limits the maximum number of simultaneous connections to handle and also affects memory usage for the connection cache
- default is (about) 1365 which is oversized for all but the largest systems. Embedded systems might setserver.max-connections = 16
or lowerserver.max-worker = 0
should generally be left unset (or "0"), as CPU bottlenecks are usually elsewhereserver.follow-symlink = "enable"
(default) should be left enabled. If such restrictions are required, prefer to run a separate lighttpd instance under a separate user account, and enforce more restrictive file access permissions.ssl.read-ahead = "disable"
(default) is strongly recommended for slower, embedded systems which process TLS packets more slowly than network wire-speed. For faster systems, test ifssl.read-ahead = "enable"
improves performance (or not)- disable h2c support to disable inadvertent h2c smuggling (security) if h2c is not expected and lighttpd is running behind a proxy (e.g. HAProxy):
server.feature-flags += ( "server.h2c" => "disable" )
- high-resolution timestamps are useful if the data are used, but have a slight impact on performance and CPU usage to collect the high-resolution time and to stringify the high-resolution time for logging. If not using the data, consider leaving
server.feature-flags
"metrics-high-precision" => "disable"
(default) and not using high-resolution format-specifiers inaccesslog.format
templates. Alternatively, enable temporarily (and restart lighttpd) for a period of time during a metrics collection session, evaluate the data, and then return to the disabled default setting.
- minimize conditional processing (but not at the cost of proper functionality)
- prefer prefix (
=^
) or suffix (=$
) conditions (lighttpd 1.4.65) where prefix/suffix matches can replace simple regex conditions (=~
,!~
) - more conditions means more config processing at runtime
- more conditions means more memory used by config per request
- lighttpd 1.4.62 restricts extra memory used by regexes to conditions containingurl.redirect
orurl.rewrite*
directives containing%X
substitutions.
- Reduce or replace use of%X
substitutions where possible to lower memory use per request.
- Use lighttpd extended replacement patterns where possible instead of%X
substitutions.
- e.g. prefer${url.authority}
in regex replacement instead of%0
inside a capturing$HTTP["host"] =~ "(.*)"
condition. - avoid repeating conditions and its opposite by joining them into if/else
<condition> { ... } else { ... }
<condition> { ... } else <condition> { ... } else { ... }
- sometimes it may take fewer config lines to set a config option once in the global scope and then, where necessary, to unset the option in a small number of conditions rather than leaving the default in the global scope and enabling the config option in many more conditions
- having no config conditions will be among the fastest configs to be processed, but config processing at runtime is fast and is not typically a bottleneck
- prefer prefix (
- dynamic backends (mod_proxy, mod_fastcgi, mod_scgi, mod_ajp13, ...)
- prefer to use unix domain sockets (instead of TCP sockets) for connections from lighttpd to backends running on the same host
- lighttpd can listen on a unix domain socket (
server.bind = "/path/to/lighttpd.sock"
) and lighttpd mod_proxy can act as a reverse-proxy to a backend lighttpd server. Use with mod_extforward to preserve client remote address for the backend. - CGI/1.1 environment (or equivalent) is created for many dynamic backends, excluding mod_proxy. The CGI/1.1 environment includes SERVER_ADDR. If
server.bind = "*"
or other wildcard address, then obtaining SERVER_ADDR costs a system call togetsockname()
. Ifserver.bind = "127.0.0.1"
or other specific IP address (not a wildcard address), then the syscall is elided. - Use fast, in-memory filesystem for temporary files, with a fallback to a large filesystem, e.g. on Linux:
server.upload-dirs = ("/dev/shm", "/var/tmp")
- mod_fastcgi
- Recommended: use PHP-FPM (FastCGI Process Manager), which is available as a package in many OS distros
- If not using PHP-FPM, then see Docs_PerformanceFastCGI- lighttpd provides mechanisms for lighttpd to start up PHP backends, and that works well, but PHP-FPM is the modern and recommended mechanism to manage PHP backends
- mod_rewrite and mod_redirect: short-circuiting (when using a sequence of regexes)
- consider putting static file matches (passed through unmodified) first, and using a blank target to indicate no modification
- consider using a blank match as a catch-all, rather than "^(.*)", which will still match all, but without the regex
url.rewrite-once = (
"^/static/|\.(?:css|jpg)$" => "",
"" => "/index.php${url.path}${qsa}"
)
- consider
url.rewrite-if-not-file
instead ofurl.rewrite-once
when configuring a catch-all if a static file does not exist.
The above example could be written as:
url.rewrite-if-not-file = (
"" => "/index.php${url.path}${qsa}"
)
- mod_indexfile: reduce the number of entries in index-file.names, if mod_indexfile is enabled
index-file.names = ("index.html")
as a list of one or two entries rather than a list of, say, 10 differenent file extensions
- cache tuning
- stat_cache: default
server.stat-cache-engine = "simple"
works well for typical usage and cachesstat()
results for 1-2 seconds
Test withserver.stat-cache-engine = "inotify"
orserver.stat-cache-engine = "kqueue"
forstat()
results to be cached longer (16 seconds) - mod_auth: set
auth.cache = ("max-age" => "600")
to cache passwords (default disabled), but acknowledge changes to your security posture if enabling the cache. (since lighttpd 1.4.56) - mod_deflate: set
deflate.cache-dir
to cache (and reuse) compressed static assets based on ETag (since lighttpd 1.4.56) - mod_dirlisting: set
dir-listing.cache = ( ... )
to configure caching of generated directory listings (since lighttpd 1.4.60)
- stat_cache: default
- do not sacrifice security to save a few CPU cycles
server.http-parseopts*
option defaults are recommended, and are very fast- disabling
server.http-parseopts*
might save a few CPU cycles, but is an anti-pattern for secure configurations server.http-parseopts*
options should be modified only when the functionality needs to be tuned for proper site operation- ETag response headers are used in HTTP/1.1 conditional caching. ETag response headers are also required for mod_deflate and strongly recommended with mod_webdav. While lighttpd ETag generation for static content can be disabled for micro-benchmarking purposes, ETag generation (default enabled) is recommended for production use (
etag.use-inode
,etag.use-mtime
,etag.use-size
)
- ensure that
mimetype.assign
assigns Content-Type to all static resources with e.g. catch-allmimetype.assign += ("" => "application/octet-stream")
If a mimetype is not explicitly assigned to static resources, lighttpd does not sendETag
orLast-Modified
with the response.
https://anexia.com/blog/en/the-tale-of-lighttpd-not-sending-the-last-modified-header/ - compile lighttpd with mmap support (
./configure --enable-mmap
) to improve mod_deflate performance
lighttpd configuration for use of operating system (OS) features¶
lighttpd generally chooses optimal defaults for the OS on which it is running. Prefer lighttpd defaults unless something is not functioning correctly. (Please report bugs and include your platform information if the lighttpd OS defaults are not working correctly.)server.event-handler
(e.g. epoll, kqueue, event ports, devpoll, poll, ...)server.network-backend
(e.g. sendfile, writev, write)
lighttpd configuration tuning for high-traffic sites with a large number of connections¶
- test with
server.max-fds = 16384
(or higher) and OS system and/or per-userulimit -Hn
might need to be adjusted to allow this or higher values.
For each 4k increase inserver.max-fds
, lighttpd uses an additional ~100 kb of memory for internal structures, not including memory used by each active connection. (In other words, there is a marginal cost for using very high values when there are not nearly so many simultaneous open connections).server.max-connections
is calculated to be 1/3 ofserver.max-fds
ifserver.max-connections
is not configured.
lighttpd configuration tuning for low-memory systems¶
- test with
server.max-fds = 128
(or lower) - test with
server.max-connections = 16
(or lower) - test with
server.listen-backlog = 16
(or lower) - (default)
server.stat-cache-engine = "simple"
- (default)
ssl.read-ahead = "disable"
- support for the HTTP/2 protocol (enabled by default in lighttpd 1.4.59) uses more memory than HTTP/1.1;
low-memory systems might choose to disable HTTP/2 protocol support:server.feature-flags += ("server.h2proto" => "disable")
- lighttpd running with glibc configures the glibc malloc
sbrk
padding to 512k. (glibc default for malloptM_TOP_PAD
is 128k.)
With lighttpd 1.4.68 and later, thesbrk
padding is set to 128k ifserver.max-connections = 16
(or lower).
Export env varMALLOC_TOP_PAD_=131072
in the environment before starting lighttpd to get similar behavior with other configs. - lighttpd mod_dirlisting
dir-listing.sort = "disable"
(since 1.4.74) can reduce memory used to generate directory listing, which may be useful for large directories.dir-listing.sort = "disable"
may be used withserver.stream-response-body = 2
to further reduce memory use when temporary files are directed to tmpfs (in-memory) filesystem, e.g. ifserver.upload-dirs = ("/tmp")
and/tmp
is tmpfs. On tiny, resource-constrained systems, using mod_indexfile instead of mod_dirlisting is even more efficient, as long as you have an out-of-band process to updateindex.html
when the directory changes.
lighttpd configuration tuning for lighttpd behind a reverse proxy¶
- prefer to configure mod_extforward
extforward.hap-PROXY
for lighttpd instances behind HAProxy or load balancers supporting the HAProxy PROXY protocol - some reverse proxies may be configured to reuse same HTTP/2 connection for multiple clients
- Some mod_auth mitigations for brute-force attacks from a single client on a connection (default) might adversely affect multiple clients reusing the same connection from a reverse proxy.
- On connections from reverse proxy shared among multiple clients, consider disabling some mod_auth mitigations for brute-force attacks
server.feature-flags += ("auth.http-goaway-invalid-creds" => "disabled")
server.feature-flags += ("auth.delay-invalid-creds" => "disable")
lighttpd configuration tuning for traffic shapping (download rate-limiting)¶
Traffic Shapingconnection.kbytes-per-second
server.kbytes-per-second
lighttpd configuration tuning for timeouts¶
To free up connections more quickly, tune down the idle timeouts for how long lighttpd waits to read or write to the client (when lighttpd is trying to read or write), or how long lighttpd waits for the next keep-alive request, and for how many keep-alive requests, before lighttpd closes the connection. A value of 0 disables an idle timeout and is not recommended.server.max-read-idle = 60
server.max-write-idle = 360
server.max-keep-alive-idle = 5
server.max-keep-alive-requests = 1000
(default 1000 with lighttpd 1.4.65; default 100 with lighttpd 1.4.46 - lighttpd 1.4.64; default 10 before lighttpd 1.4.46)
Generally, server.max-keep-alive-requests
should not be set to 0 since setting up a new TCP connection takes more resources than keeping an open idle fd, especially if the connection is over TLS.
lighttpd configuration tuning for benchmarking¶
Please understand each of these settings before considering its use outside of a benchmarking environment. These settings might not be appropriate for production environments.
- benchmarking: tune Linux TCP stack
Temporary settings are applied immediately and reset to defaults upon reboot.
/sbin/sysctl net.ipv4.tcp_fin_timeout=1
/sbin/sysctl net.ipv4.tcp_low_latency=1
/sbin/sysctl net.ipv4.tcp_slow_start_after_idle=0
/sbin/sysctl net.ipv4.tcp_tw_reuse=1
- benchmarking: tune Linux CPU frequency scaling
Take note of current power settings, e.g.
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
powersave
powersave
Temporary settings are applied immediately and reset to defaults upon reboot.
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor >/dev/null
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
performance
performance
Additional reference: https://wiki.archlinux.org/title/CPU_frequency_scaling
- benchmarking: tune Linux CPU vulnerability mitigations
Temporarily disable Spectre, Meltdown, and other CPU vulnerability mitigations by temporarily appendingmitigations=off
to kernel command line during boot.
WARNING: Disabling mitigations is generally inappropriate for production environments.
https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html and search the doc for "mitigations="
CPU vulnerability mitigations can reduce benchmark performance by some 25%-40%, but running production systems with CPU vulnerability mitigations enabled is strongly recommended. A cracked system can be much worse than a downed system. Prefer secure and robust systems. (Benchmarking results with amazing numbers sure are fun, though!)
Platform-Specific Notes¶
Note: the following is old and possibly out-dated. Please consider only as a starting point for further testing.
Linux¶
Disabling the TCP options reduces the overhead of each TCP packet and might
help to get the last few percent of performance out of the server. Be aware that
disabling these options most likely decreases performance for high-latency and lossy
links.
- net.ipv4.tcp_sack = 0
- net.ipv4.tcp_timestamps = 0
Note: Be carefull with net.ipv4.tcp_timestamps. It caused massive problems for me under benchmark load with a high count of concurrent connections.
Increasing the TCP send and receive buffers will increase the performance a
lot if (and only if) you have a lot of large files to send.
- net.ipv4.tcp_wmem = 4096 65536 524288
- net.core.wmem_max = 1048576
If you have a lot of large file uploads, increasing the receive buffers will help.
- net.ipv4.tcp_rmem = 4096 87380 524288
- net.core.rmem_max = 1048576
Some things that a high-traffic site found useful:
# These ensure that TIME_WAIT ports either get reused or closed fast. net.ipv4.tcp_fin_timeout = 1 net.ipv4.tcp_tw_recycle = 1 # TCP memory net.core.rmem_max = 16777216 net.core.rmem_default = 16777216 net.core.netdev_max_backlog = 262144 net.core.somaxconn = 262144 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_orphans = 262144 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2 # you shouldn't be using conntrack on a heavily loaded server anyway, but these are # suitably high for our uses, insuring that if conntrack gets turned on, the box doesn't die net.ipv4.netfilter.ip_conntrack_max = 1048576 net.nf_conntrack_max = 1048576
Keep in mind that every TCP connection uses the configured amount of memory for socket
buffers. If you've got many connections this can quickly drain the available memory.
See http://www.acc.umu.se/~maswan/linux-netperf.txt for more information on these parameters.
FreeBSD¶
On FreeBSD you might gain some performance by enabling accept filters. Just
compile your kernel with:
options ACCEPT_FILTER_HTTP
or just load it using
kldload accf_http
For more ideas about tuning FreeBSD read: tuning(7)
Reducing the recvspace should always be ok if the server only handles HTTP
requests without large uploads. Increasing the sendspace would reduce the
system load if you have a lot of large files to be sent, but keep in mind that
you have to provide the memory in the kernel for each connection. 1024 * 64KB
would mean 64MB of kernel RAM. Keep this in mind.
- net.inet.tcp.recvspace = 4096
Updated by gstrauss 12 months ago · 46 revisions