Caddy can be configured to add HTTP Basic Authentication to any route using a simple directive, and the surprising part is how little configuration it actually takes to implement robust security.

Let’s see it in action. Imagine you have a simple Caddyfile serving static files:

:8080 {
    root * /var/www/html
    file_server
}

Now, you want to protect the /admin route with Basic Auth. You add a new block specifically for that route:

:8080 {
    root * /var/www/html
    file_server

    route /admin* {
        basic_auth {
            user myuser passwordmypassword
        }
        root * /var/www/admin
        file_server
    }
}

When you reload Caddy (caddy reload --config Caddyfile), any request to http://localhost:8080/admin or http://localhost:8080/admin/something will now prompt for a username and password. Enter myuser and passwordmypassword, and you’re in. Without them, you’ll get a 401 Unauthorized response.

The magic here is the basic_auth directive. It’s a Caddy-native way to enforce HTTP Basic Authentication. Caddy handles the encoding and decoding of credentials, the generation of the WWW-Authenticate header, and the validation of the provided username and password. You don’t need to set up an external authentication service or write complex middleware. The user subdirective takes the username and the password directly.

The mental model is straightforward: Caddy processes requests sequentially based on the order of directives and routes. When a request matches the /admin* route, Caddy first applies the basic_auth directive. If authentication fails, the request is immediately terminated with a 401. If it succeeds, Caddy then proceeds to the subsequent directives within that route, like setting the root and serving files. You can nest basic_auth within other directives or even apply it globally if you wish, though route-specific protection is often more practical.

For more complex scenarios, like managing many users or rotating passwords, you can move the credentials to a separate file. Create a file, say auth.htpasswd, with content like this:

myuser:$apr1$xxxxxxxxxxxxxxxx$yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
admin:$apr1$zzzzzzzzzzzzzzzz$wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww

This format is compatible with Apache’s htpasswd utility. You can generate such a file using htpasswd -c auth.htpasswd myuser and then htpasswd auth.htpasswd admin for subsequent users. The Caddyfile then becomes:

:8080 {
    root * /var/www/html
    file_server

    route /admin* {
        basic_auth {
            import auth.htpasswd
        }
        root * /var/www/admin
        file_server
    }
}

This import directive tells Caddy to load the user credentials from the specified file. This is particularly useful for security, as it keeps sensitive passwords out of your main Caddyfile and allows for easier management of multiple users. Caddy internally uses a cryptographically secure hashing mechanism (similar to crypt(3) or bcrypt depending on the format) and compares the provided password hash against the stored one.

The basic_auth directive itself can be configured with additional options, such as setting a realm, which is the string displayed to the user in the login prompt. For example:

basic_auth {
    user myuser passwordmypassword
    realm "Admin Area"
}

This would change the prompt to "Admin Area". It’s a small detail, but it helps users understand what they are logging into, especially if Caddy is serving multiple protected resources.

The next concept you’ll likely encounter is how to handle more sophisticated authentication schemes, such as token-based authentication or integrating with external identity providers, which Caddy also supports through its plugin ecosystem.

Want structured learning?

Take the full Caddy course →