Self-hosted service
emikulic/darkhttpd avatar
emikulic/darkhttpd

darkhttpd: A Small Static Web Server for Quick Deployments

When you need a web server in a hurry.

1,351 stars120 forksCISC

At a glance

What is it?
darkhttpd is a compact C web server for serving static files from one directory or a single file. It fits temporary documentation sites, local previews, and small internal deployments, but it does not support CGI.
Who is it for?
Adopt darkhttpd for a small static site, local preview, or restricted single-file service when a minimal binary and an event loop are more useful than application-server features. Do not choose it for CGI, dynamic request handling, or a deployment where you need a richer configuration system.
Can I use it commercially?
Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 60 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why darkhttpd Fits Static File Delivery

darkhttpd addresses a narrow but common problem: putting an existing directory of static files on the network without configuring a general-purpose application server. Its README describes it as a server for situations when you need a web server quickly. The repository contains a C implementation, a Makefile, a Dockerfile, and a small `docker/` directory, so the deployment surface is centered on one executable rather than a larger runtime stack.

The server is aimed at users who already have files to publish. It can serve a directory, generate a directory listing, or expose one file through `--single-file`. The supported request methods are HTTP GET and HEAD. For static downloads, it also supports Range requests for partial content, `If-Modified-Since`, and persistent connections through Keep-Alive. Those features are directly relevant to serving music files, resuming downloads, or publishing a simple site.

It is not a dynamic web application server. The README explicitly lists "Only serves static content - no CGI" as a limitation. There is no documented support for routing requests into application code, uploading files, interpreting server-side scripts, or querying a database. A deployment that needs those capabilities would need another project or an external service in front of darkhttpd.

The Single-Threaded Event Loop and File Serving Model

The documented architecture is deliberately small: darkhttpd uses an event loop and a single thread, with no `fork()` or pthreads. That design avoids the process or worker arrangement used by some servers, but it also means the project is not presenting itself as a general multi-worker application server. The README highlights its small memory footprint and its use of `sendfile()` on FreeBSD, Solaris, and Linux. The exact implementation is in `darkhttpd.c`, while the repository layout confirms that the server is not split into a large service framework.

The command line supplies the content root. From that root, darkhttpd serves static paths and can generate directory listings. `--index` changes the filename used for directory indexes, with `default.htm` shown as an example instead of `index.html`. `--mimetypes` loads additional extension-to-type mappings from a file. The README's example maps `.dat` to `text/plain`, which is useful when the built-in mapping does not match a local file convention.

The server can restrict its listening address with `--addr`, limit simultaneous connections with `--maxconn`, and time out idle connections according to the security features listed in the README. It also supports custom response headers through `--header`, and 301 redirects based on the `Host` header through `--forward` and `--forward-all`. These options make it more than a bare file browser, but they remain command-line controls rather than a documented configuration language.

Building darkhttpd and Serving the First Directory

The build command is:

bash
make

The README says this produces the `darkhttpd` binary. A compiler selection can be supplied through the `CC` environment variable:

bash
CC=gcc make

For the first real use, serve `/var/www/htdocs` on the default port. The README states that the default is port 80 when running as root and 8080 otherwise:

bash
./darkhttpd /var/www/htdocs

To choose a different port, pass the content root followed by `--port`:

bash
./darkhttpd ~/public_html --port 8081

A quick local check can bind specifically to `127.0.0.1` and use port 8080. This is useful when the service should not listen on every available interface:

bash
./darkhttpd ~/public_html --port 8080 --addr 127.0.0.1

The README does not document a separate initialization command or required runtime dependency for the normal binary. Running the executable without arguments displays the full command-line option list:

bash
./darkhttpd

Docker Deployment and the Static-File Boundary

The repository includes a multi-stage `Dockerfile` for packaging darkhttpd as a small static image. The build stage uses Alpine and installs `build-base`, copies the repository into `/src`, compiles `darkhttpd`, and strips the executable. The runtime stage is `scratch`, so the final image contains the static binary plus the passwd and group files copied from the repository. It exposes port 80 and starts `/darkhttpd` with the current directory as the served root, `--chroot`, and the `nobody` user and group.

Build the image with the command given in the README:

bash
docker build -t darkhttpd .

Then mount the files to publish. The following example serves the current user's `dev/mywebsite` directory read-only and maps it to port 8080 on the host:

bash
docker run -p 8080:80 -v ~/dev/mywebsite:/var/www/htdocs:ro darkhttpd

The read-only volume is a sensible operational choice when the published files should not be modified by the container, although the README only requires a mounted content directory and does not prescribe a broader deployment architecture. The Docker setup makes the single-binary model visible: the runtime image is based on `scratch`, not a full Linux distribution.

This boundary also explains where darkhttpd is the wrong tool. A container can package the server, but it cannot turn it into a CGI or application server. Teams needing dynamic routes, forms backed by application code, or runtime templating would need to add software that the repository does not document. The Dockerfile's defaults also show that deployment choices, such as the served directory and unprivileged account, are part of the container command rather than a rich server configuration file.

Security Options, Access Logs, and Operational Limits

darkhttpd includes several controls that are relevant when publishing files beyond a trusted local preview. The README lists access logging, including Referer and User-Agent information. A log file can be selected with `--log`:

bash
./darkhttpd ~/public_html --log access.log

The server can run with a changed identity through `--uid` and `--gid`:

bash
./darkhttpd /var/www/htdocs --uid www --gid www

For Unix systems where root privileges are available, the documentation also shows `chroot`:

bash
./darkhttpd /var/www/htdocs --chroot

The project describes `chroot` support as available on FreeBSD 14 and later when running as non-root. The README separately states that `--accf` uses FreeBSD's `acceptfilter`, after loading the `accf_http` kernel module. Those features are platform-specific, so availability should be checked against the target operating system.

The listed security behavior also includes rejecting `/../` sniffing, timing out idle connections, and dropping overly long requests. These are server behaviors, not evidence that the server is suitable for every hostile or public workload. The README does not document authentication, authorization, TLS termination, rate limiting, or a vulnerability disclosure process. Operators should therefore not infer those controls from the presence of `chroot` or privilege dropping. The absence of CGI reduces the server's attack surface compared with a dynamic server, but it does not replace network policy, file permissions, or an explicit decision about how the listening socket is exposed.

Maintenance Signals and Upgrade Cost

The repository is not archived. Its last push was on 2026-08-24, which is within the six-month period allowed for describing a project as actively maintained. The latest listed release is `darkhttpd 1.17` from 2025-07-24, following `darkhttpd 1.16` from 2024-02-28 and `darkhttpd 1.15` from 2024-01-18. Those release dates provide a maintenance signal, but the README does not describe a long-term support policy or a compatibility promise across releases.

The upgrade cost appears operationally small for straightforward deployments because the build produces a single C binary and the runtime documentation does not require `inetd` or `ucspi-tcp`. A source-based deployment can rebuild with `make`, while a Docker deployment can rebuild the image and replace the binary through its normal image workflow. The repository's test target is also available:

bash
make test

The README documents a FreeBSD-specific alternative when the test setup needs adjustment:

bash
ASAN_OPTIONS=" " PYTHON=python3.11 make test

The project is licensed under ISC. The repository includes a `COPYING` file, and the README identifies the license as ISC. That is a permissive license classification, but users still need to read the license text and review their own compliance obligations rather than treating this article as legal advice.

The practical upgrade concern is configuration drift. Port selection, address binding, MIME mappings, redirects, headers, privilege changes, and container arguments are all explicit command-line or file inputs. Before replacing a binary, an operator should compare those arguments with the files being served and confirm that the Docker image still exposes the intended port and user identity.

Choosing Between darkhttpd and a General Web Server

darkhttpd is most distinct from a general web server in its scope. It accepts a content root and serves files from it, with directory listings, partial content, conditional requests, custom headers, and selected 301 redirects. Its event loop and single-threaded design keep the implementation compact. A general-purpose server typically offers a larger set of application and deployment mechanisms, but that larger surface is not documented here and should not be attributed to darkhttpd as a comparison of concrete features.

The practical alternative for dynamic sites is a static-site generator followed by a conventional web server, or a separate application server for CGI or other runtime behavior. That approach changes the architecture: content is prepared before serving, and request processing is handled by another component. darkhttpd instead keeps the serving model close to the files themselves.

For a single-file release or a small directory on a private network, the direct executable is easier to reason about than a larger service configuration. For a site that needs content negotiation, access control, TLS configuration, request rewriting, or application execution, the missing features in the README matter more than darkhttpd's small footprint. The choice is therefore about workload fit, not a general claim that one server is better.

Editorial conclusion

Adopt darkhttpd for a small static site, local preview, or restricted single-file service when a minimal binary and an event loop are more useful than application-server features. Do not choose it for CGI, dynamic request handling, or a deployment where you need a richer configuration system. Verify first that your required files, MIME mappings, security options, and expected release artifact work with the target operating system. The project is not archived, its last push was on 2026-08-24, and the latest listed release is v1.17 from 2025-07-24.

Frequently asked questions

How do I build darkhttpd?

Run `make` in the repository. The Makefile compiles `darkhttpd.c` into the `darkhttpd` executable, and the README also shows `CC=gcc make` when the default compiler name is not suitable.

How do I serve a directory on port 8081?

Run `./darkhttpd ~/public_html --port 8081`. The first argument identifies the directory to serve, and `--port 8081` selects the listening port.

Does darkhttpd support CGI or other dynamic content?

No. The README lists only static content support and explicitly says that CGI is not supported.

Can darkhttpd run as an unprivileged user?

The README documents `--uid` and `--gid`, with an example that changes both to `www`. The Docker image also starts the server with `--uid nobody --gid nobody`.

Does darkhttpd provide a configuration file?

The README emphasizes that no configuration file is required and presents the content root and command-line options as the main controls. Additional MIME types can be supplied through a file passed to `--mimetypes`.

Official sources

  1. emikulic/darkhttpd on GitHub
  2. License: ISC
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/emikulic-darkhttpd.svg)](https://hysenlabs.com/projects/emikulic-darkhttpd)