httpd

## Name

httpd — UCSPI-TCP HTTP server for static content

## Synopsis

`httpd` {`root`}

## Description

httpd prints requested public files from the `root` directory hierarchy. The Bernstein convention is for `root` to be `/public/file`, but it can use other conventional locations such as `/home/publicfile/public` or `/var/www`.

httpd accepts GET and HEAD requests on standard input, and responds on standard output according to the HTTP version employed in the request:

- HTTP/0.9: httpd prints the requested file and exits.

- HTTP/1.0: httpd prints an HTTP/1.0 header and the requested file. Then it exits.

- HTTP/1.1: httpd prints an HTTP/1.1 header and the requested file in chunked format. Then it waits for further requests.

Unless the `OLDPROTOCOLS` environment variable is set, httpd only accepts the HTTP/1.1 request format, rejecting all others with a 426 error.

If the file is unopenable or if httpd does not like the request, httpd prints an error message and exits. If the URL length exceeds the pathconf(3) `_PC_PATH_MAX` value for the `root` directory, httpd prints an error message and exits. If httpd runs out of memory, encounters an I/O error, or does not receive an input packet within 60 seconds, it exits silently.

httpd also prints local log information on standard error. Unless the `LOGUNSUPPORTED` environment variable is set, httpd does not output any log messages for requests with unsupported features.

httpd has exact-prefix support for `If-Modified-Since:` it uses code 304 if the `Last-Modified` contents are an exact prefix of the `If-Modified-Since` contents.

Normally httpd is run under a UCSPI-TCP server program (tcp-socket-accept, s6-tcpserver, or tcpserver spawning a server program per connection) to handle HTTP connections from hosts around the Internet. It can also be run under a UCSPI-SSL server program.

### File handling

A request for `http://`v`/`f``, where `f` does not end with a slash, refers to the file named `./`v`/`f`` inside the `root` directory hierarchy. A request for `http://`v`/`f`/`, ending in a slash, refers to the file named `./`v`/`f`/index.html`. httpd always converts the host name ``v`` to lowercase. HTTP/0.9 requests and old HTTP/1.0 requests do not specify a host name; in this case httpd uses the host name `0`.

If it successfully opens the file, httpd uses the file name to select a file type for HTTP/1.0 and HTTP/1.1.

### Unsupported features

httpd does not support file modification requests such as POST. httpd does not support SSI or CGI.

httpd rejects requests specifying `Content-Length`, `Transfer-Encoding`, `Expect`, `If-Match`, `If-None-Match`, or `If-Unmodified-Since`. httpd rejects requests with URLs containing query or fragment identifier parts.

httpd does not generate its own directory listings, even if `index.html` does not exist. httpd rejects requests for directory names without terminating slashes; it does not redirect the requests.

## Security

httpd chroots to `root` when it starts. It then sets its group id and user id to the numbers given in environment variables `GID` and `UID`, as set by envuidgid (or equivalent). httpd does not allow dots immediately after slashes in file names. It changes these dots to colons before attempting to open the file.

### Note

Earlier versions of httpd required the `/etc/leapsecs.dat` to be copied under `root` . This file is now read before the chroot.

httpd will refuse to read a file if the file

- is unreadable to user;

- is unreadable to group;

- is unreadable to world;

- is world-executable without being user-executable; or

- is anything other than a regular file: a directory, socket, device, etc.

Given that many clients are permissive HTTP/1.1 clients, the possibility that a HTTP/0.9 response could be misinterpreted as including headers, thus opening the possibility for headers to be faked by body content, is now a significant one and is what motivated curl in 2018 making HTTP/0.9 responses errors unless HTTP/0.9 is explicitly opted-in.

## History

httpd was originally part of Daniel J. Bernstein's publicfile toolset in 1999. HTTP/1.1 was formalized that same year, having been proposed in 1997.

The default mode of only supporting HTTP/1.1 was introduced in 2025.

By that point: legitimate HTTP/0.9 and HTTP/1.0 traffic had been widely observed as having markedly dropped off since 2010, with even the (good-actor) robots having switched to HTTP/1.1; wget had switched to HTTP/1.1 by default in 2010; libcurl had supported HTTP/1.1 since 2001, had switched to it as the default in 2013, and had dropped HTTP/0.9 for security reasons, unless explicitly opted in, in 2018; libfetch had defaulted to HTTP/1.1; and Dominic Lovell had reported in the 2021 Web Almanac by the HTTP Archive essentially 0% use of HTTP/0.9 and HTTP/1.0. Moreover, old clients that could not use HTTP/1.1 were cut off from the modern WWW anyway for other reasons (the push for widespread use of HTTPS-only, and the deprecation of old encryption standards known by old clients) and would have to be behind HTTP/1.1-speaking proxies.

Over a quarter of a century since HTTP/1.1 was invented, permissively allowing earlier protocol versions by default is no longer a practical necessity and is now rather a weakness to be targetted.

## Author

Original code and documentation by Daniel J. Bernstein. Documentation modernizations by Jonathan de Boyne Pollard.