Serve and route requests

Serve a static site

Set a document root, serve files, and verify that filesystem ownership matches the Caddy process.

10 minute lesson

~~~

Serving files is the oldest job a web server has, and in Caddy it takes two directives.

root sets the directory that request paths are resolved against. file_server does the actual work: it takes the request path, finds the matching file under the root, and sends it back.

:8080 {
  root * ./public
  file_server
}

The * after root is a matcher meaning every request. Create some content and start the server:

mkdir -p public
echo '<h1>My site</h1>' > public/index.html
caddy run --config Caddyfile

Verify both the happy path and a miss:

curl http://127.0.0.1:8080/
curl -i http://127.0.0.1:8080/missing.html

The first request returns your HTML — file_server serves index.html for directory requests automatically. The second returns 404 Not Found, which is what you want: no hints about what else exists on disk.

Why two separate directives? Because other handlers use the root too. try_files, php_fastcgi, and rewrites all resolve paths against it. Setting it once keeps every handler pointed at the same tree.

Caddy protects you from path traversal, so a request for /../../etc/passwd cannot escape the root. But it follows symbolic links. A symlink inside ./public that points at /etc will happily be served. Audit links and permissions before you serve a real directory.

The classic production failure is permissions. The packaged service runs as the caddy user, and home directories are often mode 700. Your files exist, your config is valid, and every request still fails. Check the runtime log with journalctl -u caddy, then give the service user read access to the site tree. Don’t make the tree world-writable out of frustration — grant the smallest access that fixes the error.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →