Caddy can serve an unlimited number of domains from a single instance by leveraging its automatic HTTPS and dynamic configuration capabilities.

Let’s see it in action. Imagine you have two entirely different websites, example.com and another-site.net, and you want to host both on the same server running Caddy.

Here’s a minimal Caddyfile to achieve this:

example.com {
    root * /var/www/example.com
    file_server
}

another-site.net {
    root * /var/www/another-site.net
    file_server
}

When Caddy starts with this configuration, it will:

  1. Listen on ports 80 and 443.
  2. For example.com:
    • If a request comes in for example.com on port 80, Caddy will respond with an HTTP 301 redirect to https://example.com.
    • If a request comes in for https://example.com, Caddy will automatically obtain an SSL certificate from Let’s Encrypt (or another ACME provider).
    • It will then serve files from the /var/www/example.com directory.
  3. For another-site.net:
    • It repeats the same process: redirects HTTP to HTTPS, obtains a certificate for another-site.net, and serves files from /var/www/another-site.net.

The magic here is that Caddy’s configuration is designed to be dynamic and host-aware. When a request arrives, Caddy inspects the Host header of the incoming HTTP request. Based on that header, it looks up the corresponding site block in its configuration. If a matching block is found, Caddy applies the directives within that block to handle the request. This lookup is extremely efficient.

What problem does this solve? Without Caddy, managing multiple domains on a single web server typically involves a lot of manual configuration. You’d need to:

  • Set up separate virtual host configurations for each domain in your web server (like Apache or Nginx).
  • Manually obtain and manage SSL certificates for each domain, often using tools like certbot, and configure your web server to use them. This involves renewal management and can be error-prone.
  • Ensure that your server’s IP address is correctly associated with all these domains in DNS.

Caddy abstracts all of this away. Its primary design goal is to make running a secure website as simple as possible. The Caddyfile is a human-readable configuration format that allows you to define multiple "sites." Each site block is identified by one or more hostnames.

The internal mechanism for this is Caddy’s request multiplexing and certificate management. Caddy uses the SNI (Server Name Indication) extension in TLS handshakes to determine which certificate to present to the client before the HTTP request is even fully processed. For HTTP requests, it relies on the Host header. Once it knows which domain the request is for, it can apply the correct site configuration, including serving the right files, proxying to the right backend, or applying other middleware.

The automatic HTTPS feature is a cornerstone. Caddy defaults to using Let’s Encrypt. When it sees a new domain in its configuration that it doesn’t have a certificate for, it automatically initiates the ACME challenge process. It can solve both HTTP-01 and TLS-ALPN-01 challenges. For HTTP-01, Caddy will temporarily serve a specific file at a specific path (/.well-known/acme-challenge/) that Let’s Encrypt queries. For TLS-ALPN-01, it can even handle the challenge over TLS itself. Once validated, Caddy stores the certificate and key locally and automatically renews them before they expire. This means you rarely, if ever, have to think about certificate management for your domains.

The root directive specifies the filesystem path from which to serve static files for that specific domain. The file_server directive enables Caddy’s static file server. You can also use reverse_proxy to send traffic to backend applications, and Caddy will handle multiple backends for different domains, all with automatic HTTPS.

A common point of confusion is how Caddy handles requests when multiple domains in the Caddyfile might match a given request. For example, if you have example.com and *.example.com configured. Caddy has a specific matching order. Exact hostnames are preferred over wildcard hostnames. If multiple exact matches exist, Caddy will typically pick one based on its internal configuration loading order, but it’s best practice to avoid overlapping wildcard and exact matches for the same apex domain. If you needed to serve www.example.com and example.com differently, you’d list them as separate site blocks, or use a single block with both hostnames:

www.example.com, example.com {
    root * /var/www/example.com
    file_server
}

This ability to define distinct configurations for each domain, coupled with automatic certificate management, makes Caddy incredibly powerful for hosting multiple websites.

The next step in managing multiple domains often involves understanding how to use environment variables or JSON configuration to manage these Caddyfile directives programmatically.

Want structured learning?

Take the full Caddy course →