FreeBSD.software
Home/Guides/Lighttpd on FreeBSD: Lightweight Web Server Review
review·2026-04-09·10 min read

Lighttpd on FreeBSD: Lightweight Web Server Review

In-depth review of Lighttpd on FreeBSD: minimal resource footprint, configuration system, FastCGI integration, static file serving, and comparison with NGINX.

Lighttpd on FreeBSD: Lightweight Web Server Review

Lighttpd (pronounced "lighty") is a web server built for speed and minimal resource consumption. It was created in 2003 by Jan Kneschke as a proof of concept for handling the C10K problem -- serving 10,000 concurrent connections on a single server. Over two decades later, Lighttpd remains one of the leanest web servers available, with a memory footprint that makes it viable on hardware where NGINX or Apache would be wasteful. On FreeBSD, Lighttpd uses kqueue for event-driven I/O, integrates with the rc.d service framework, and is available as both a binary package and a port. This review covers Lighttpd's architecture, FreeBSD installation and configuration, FastCGI integration, static file performance, and where it fits relative to NGINX.

What Lighttpd Does

Lighttpd is an HTTP/1.1 web server with a focus on static content serving, FastCGI backend communication, and reverse proxying. It is single-threaded with an event-driven architecture, similar to NGINX but predating it. Lighttpd does not spawn worker processes by default -- a single process handles all connections using non-blocking I/O.

Core capabilities:

  • Static file serving -- efficient sendfile() support on FreeBSD, automatic MIME type detection, range requests, and conditional requests (If-Modified-Since, ETag).
  • FastCGI -- native FastCGI support for connecting to PHP-FPM, Python WSGI servers, Ruby, and other backends. Lighttpd was one of the first web servers to treat FastCGI as a primary interface rather than an afterthought.
  • URL rewriting -- mod_rewrite provides regex-based URL manipulation for clean URLs, redirects, and routing.
  • TLS -- native TLS support via OpenSSL or mbedTLS. SNI (Server Name Indication) for virtual hosting with per-domain certificates.
  • Virtual hosting -- name-based and IP-based virtual hosts with per-host document roots and configurations.
  • Compression -- mod_deflate for gzip and brotli compression of responses.
  • WebDAV -- mod_webdav for file management via the WebDAV protocol.
  • Authentication -- mod_auth supports basic and digest authentication with flat files, LDAP, or database backends.

What Lighttpd does not do: it does not support HTTP/2 natively in the mainline release (HTTP/2 support is available in recent versions via mod_h2), it does not have a built-in reverse proxy as capable as NGINX's, and it does not support dynamic configuration reloading without a restart (graceful restart is supported).

Installation on FreeBSD

Binary Package

sh
pkg install lighttpd

This installs the Lighttpd binary at /usr/local/sbin/lighttpd, the configuration at /usr/local/etc/lighttpd/lighttpd.conf, and the rc.d script at /usr/local/etc/rc.d/lighttpd.

Enable Lighttpd:

sh
sysrc lighttpd_enable="YES"

Ports Installation

For custom build options (specific TLS library, additional modules):

sh
cd /usr/ports/www/lighttpd make config make install clean

The ports build lets you select between OpenSSL and mbedTLS, enable or disable specific modules, and compile with debugging symbols if needed.

Verify Installation

sh
lighttpd -v lighttpd -t -f /usr/local/etc/lighttpd/lighttpd.conf

The -t flag validates the configuration file without starting the server. Always run this before starting or restarting Lighttpd in production.

Configuration

Lighttpd uses a single configuration file with an include mechanism. The syntax is unique -- not NGINX-style blocks, not Apache-style directives, but a flat key-value format with conditional blocks.

Basic Configuration

The default configuration file at /usr/local/etc/lighttpd/lighttpd.conf:

sh
server.document-root = "/usr/local/www/data/" server.port = 80 server.bind = "0.0.0.0" server.username = "www" server.groupname = "www" server.pid-file = "/var/run/lighttpd.pid" server.errorlog = "/var/log/lighttpd/error.log" accesslog.filename = "/var/log/lighttpd/access.log" server.modules = ( "mod_access", "mod_accesslog", "mod_deflate", "mod_redirect", "mod_rewrite" ) mimetype.assign = ( ".html" => "text/html", ".css" => "text/css", ".js" => "application/javascript", ".json" => "application/json", ".png" => "image/png", ".jpg" => "image/jpeg", ".gif" => "image/gif", ".svg" => "image/svg+xml", ".ico" => "image/x-icon", ".txt" => "text/plain" ) index-file.names = ("index.html", "index.htm") url.access-deny = ("~", ".inc")

Create the log directory:

sh
mkdir -p /var/log/lighttpd chown www:www /var/log/lighttpd

Virtual Hosting

Lighttpd supports virtual hosts through conditional configuration blocks:

sh
$HTTP["host"] == "example.com" { server.document-root = "/usr/local/www/example.com/" accesslog.filename = "/var/log/lighttpd/example.com-access.log" } $HTTP["host"] == "api.example.com" { server.document-root = "/usr/local/www/api/" }

Pattern matching with regex:

sh
$HTTP["host"] =~ "^(www\.)?example\.com$" { server.document-root = "/usr/local/www/example.com/" }

TLS Configuration

Enable HTTPS with a Let's Encrypt certificate:

sh
server.modules += ("mod_openssl") $SERVER["socket"] == ":443" { ssl.engine = "enable" ssl.pemfile = "/usr/local/etc/letsencrypt/live/example.com/combined.pem" ssl.privkey = "/usr/local/etc/letsencrypt/live/example.com/privkey.pem" ssl.openssl.ssl-conf-cmd = ("MinProtocol" => "TLSv1.2") ssl.openssl.ssl-conf-cmd += ("CipherString" => "HIGH:!aNULL:!MD5") }

Note: Lighttpd expects the certificate and chain in a single PEM file. Combine them if needed:

sh
cat /usr/local/etc/letsencrypt/live/example.com/fullchain.pem \ /usr/local/etc/letsencrypt/live/example.com/privkey.pem \ > /usr/local/etc/letsencrypt/live/example.com/combined.pem

Compression

Enable response compression to reduce bandwidth:

sh
deflate.mimetypes = ( "text/html", "text/css", "text/javascript", "application/javascript", "application/json", "text/xml", "application/xml", "image/svg+xml" ) deflate.allowed-encodings = ("br", "gzip", "deflate") deflate.cache-dir = "/var/cache/lighttpd/compress/"

Create the cache directory:

sh
mkdir -p /var/cache/lighttpd/compress chown www:www /var/cache/lighttpd/compress

FastCGI Integration

FastCGI is where Lighttpd shines for dynamic content. The integration is cleaner and simpler than Apache's mod_php or even NGINX's FastCGI pass configuration.

PHP-FPM

Install PHP-FPM:

sh
pkg install php83 php83-extensions sysrc php_fpm_enable="YES" service php-fpm start

Configure Lighttpd to forward PHP requests to PHP-FPM:

sh
server.modules += ("mod_fastcgi") fastcgi.server = ( ".php" => (( "socket" => "/var/run/php-fpm.sock", "broken-scriptfilename" => "enable" )) )

This sends all .php requests to the PHP-FPM socket. The broken-scriptfilename option fixes PATH_INFO handling for frameworks that rely on it.

Python via FCGI

For Python applications using flup or similar FCGI adapters:

sh
fastcgi.server = ( "/app" => (( "socket" => "/var/run/python-app.sock", "check-local" => "disable" )) )

The check-local disable is important -- it tells Lighttpd not to check for a local file before forwarding the request, which is necessary for application servers that handle all routing internally.

Spawning FastCGI Processes

Lighttpd can spawn and manage FastCGI processes directly:

sh
fastcgi.server = ( ".php" => (( "bin-path" => "/usr/local/bin/php-cgi", "socket" => "/tmp/php-fastcgi.socket", "max-procs" => 4, "bin-environment" => ( "PHP_FCGI_CHILDREN" => "4", "PHP_FCGI_MAX_REQUESTS" => "1000" ) )) )

This approach is simpler than running a separate PHP-FPM service but gives you less control over process management. For production, using PHP-FPM as an external service is recommended.

Static File Performance

Lighttpd's static file serving performance on FreeBSD is excellent. It uses sendfile() for zero-copy file transfer from disk to network socket, avoiding unnecessary copies through userspace.

Enable sendfile explicitly (usually enabled by default on FreeBSD):

sh
server.network-backend = "sendfile"

For high-traffic static file serving, tune the connection limits:

sh
server.max-connections = 4096 server.max-fds = 8192

Ensure the system allows enough file descriptors:

sh
sysctl kern.maxfiles=65536 sysctl kern.maxfilesperproc=32768

Caching Headers

Configure cache headers for static assets:

sh
$HTTP["url"] =~ "\.(css|js|png|jpg|gif|ico|svg|woff2)$" { expire.url = ("" => "access plus 30 days") }

This requires mod_expire to be loaded in the modules list.

ETag and Last-Modified

Lighttpd generates ETags and Last-Modified headers automatically for static files. Conditional requests (If-None-Match, If-Modified-Since) are handled correctly, returning 304 Not Modified when appropriate. This reduces bandwidth for returning visitors without any configuration.

Resource Footprint

This is Lighttpd's primary selling point. A running Lighttpd process with the default configuration uses roughly 2-5 MB of resident memory. Under load with thousands of connections, memory usage scales linearly but remains far below NGINX or Apache for equivalent workloads.

Typical memory usage on FreeBSD:

  • Idle: 2-3 MB RSS
  • 1,000 concurrent connections: 10-15 MB RSS
  • 10,000 concurrent connections: 40-60 MB RSS

Compare this with NGINX (4-8 MB idle, 20-40 MB under load) or Apache with prefork MPM (50-200 MB with a handful of workers). On constrained systems -- embedded devices, small VPS instances, Raspberry Pi running FreeBSD -- this difference matters.

Check Lighttpd memory usage:

sh
ps aux | grep lighttpd top -p $(cat /var/run/lighttpd.pid)

Lighttpd vs NGINX for Static Sites

The question most administrators ask is whether Lighttpd is worth considering when NGINX exists. The answer depends on what you value.

Choose Lighttpd when:

  • Memory is genuinely constrained (under 256 MB available for the web server).
  • You need a simple, reliable static file server with minimal configuration.
  • Your deployment is static-only or static + FastCGI with straightforward routing.
  • You want a single-process, single-threaded architecture for simplicity.
  • You are running on embedded FreeBSD systems or very small VPS instances.

Choose NGINX when:

  • You need HTTP/2 or HTTP/3 support with full feature parity.
  • You need advanced reverse proxy features (load balancing, health checks, circuit breakers).
  • You need a large ecosystem of third-party modules.
  • You want Lua scripting via OpenResty or njs for request processing.
  • Your team already has NGINX operational expertise.

Performance comparison:

For pure static file serving, Lighttpd and NGINX perform within 5-10% of each other on FreeBSD with kqueue. Both saturate gigabit networks easily. The difference is not in throughput but in features and memory footprint.

Service Management

Start and manage Lighttpd:

sh
service lighttpd start service lighttpd stop service lighttpd restart

Graceful restart (finishes existing connections before restarting):

sh
service lighttpd gracefulrestart

Test configuration before restart:

sh
lighttpd -t -f /usr/local/etc/lighttpd/lighttpd.conf

Check running status:

sh
service lighttpd status sockstat -4 -l | grep lighttpd

Limitations

  • HTTP/2 support -- available via mod_h2 in recent versions but not as mature as NGINX's implementation.
  • No HTTP/3 -- QUIC/HTTP/3 is not supported.
  • Single-threaded -- the single-process architecture limits throughput on multi-core systems for CPU-bound workloads. For I/O-bound static serving, this is not a practical limitation.
  • Smaller community -- fewer tutorials, fewer third-party modules, and less Stack Overflow coverage compared to NGINX.
  • Configuration syntax -- the conditional block syntax is functional but less intuitive than NGINX's location blocks for complex routing.
  • No dynamic reload -- configuration changes require a restart (graceful restart is supported, but there is a brief interruption).

Verdict

Lighttpd earns its place in the FreeBSD ecosystem as the web server for administrators who value simplicity and efficiency above all else. It does one thing extremely well: serve static files and forward dynamic requests to FastCGI backends with minimal resource consumption. On FreeBSD, the kqueue integration is solid, the pkg installation is straightforward, and the rc.d service management works as expected.

For static sites, documentation hosting, API gateways fronting FastCGI backends, and any scenario where you want a web server that barely registers in top, Lighttpd is an excellent choice. It will not replace NGINX for complex reverse proxy setups or high-feature-count deployments, but it was never designed to. If you need a lightweight, reliable web server on FreeBSD and nothing more, Lighttpd delivers exactly that.


Frequently Asked Questions

Does Lighttpd support HTTP/2 on FreeBSD?

Yes, through mod_h2 available in recent versions (1.4.56+). Load the module in your configuration with server.modules += ("mod_h2"). HTTP/2 requires TLS to be configured. The implementation is functional but less tested than NGINX's HTTP/2 support.

Can Lighttpd run in a FreeBSD jail?

Yes. Lighttpd runs in jails without modification. Ensure the jail has the necessary network access and that the document root, log directory, and PID file paths are accessible within the jail's filesystem.

How does Lighttpd compare to Caddy?

Caddy offers automatic HTTPS, HTTP/2, and HTTP/3 out of the box but uses significantly more memory (40 MB+ binary, Go runtime overhead). Lighttpd uses 2-5 MB. If automatic certificate management and modern protocol support matter, choose Caddy. If minimal resource usage is the priority, choose Lighttpd.

Can I use Lighttpd as a reverse proxy?

Yes, via mod_proxy. It supports HTTP backend proxying with basic load balancing. However, NGINX and Caddy offer significantly more capable reverse proxy features. If reverse proxying is your primary use case, Lighttpd is not the best choice.

Does Lighttpd work with Let's Encrypt on FreeBSD?

Yes. Use certbot or acme.sh to obtain certificates and configure them in Lighttpd's TLS settings. Lighttpd does not have built-in ACME support like Caddy, so you need an external tool and a cron job or periodic task for renewal. After renewal, restart Lighttpd to load the new certificate.

What is the maximum number of connections Lighttpd can handle on FreeBSD?

With kqueue, Lighttpd can handle tens of thousands of concurrent connections on FreeBSD. The practical limit depends on file descriptor limits (kern.maxfiles, kern.maxfilesperproc), available memory, and the server.max-connections setting. For most deployments, 4,096 to 16,384 concurrent connections is achievable on modest hardware.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.