Access lists & mTLS
Two ways to put something in front of a host that does not involve a login page. For the login-page version, see forward auth.
- unused
- empty
| Username | Added | Actions |
|---|---|---|
| avery | 04/01/2026 | |
| sam | 04/01/2026 | |
| priya | 22/01/2026 |
Access lists
Section titled “Access lists”Multi-account HTTP basic auth, assignable per proxy host. Passwords are bcrypt-hashed. One list can protect several hosts, and adding an account to the list grants it everywhere the list is used.
An empty list fails closed. A host whose list has no users and no IP rules refuses every request rather than serving it unauthenticated. The access lists page flags an empty list, and warns when a host is still using one; the host editor marks empty lists in its picker and warns when one is chosen.
IP rules
Section titled “IP rules”A list’s Network tab holds allow and deny rules on client addresses - single IPs, CIDR ranges or hostnames, IPv4 or IPv6 - checked from the top, where the first rule that matches decides. When no rule matches sets what everyone else gets; it defaults to deny, so a list of allow rules is an allowlist. The address is the one Caddy resolves after trusted proxies, so a CDN or load balancer in front is seen through rather than allowed or denied as a whole.
The same rules can guard an L4 host, where only they apply - a list’s users and passwords have no meaning below HTTP. Either way the rules can only see the address that reaches Caddy: with Docker’s userland proxy in the path, some setups show every client as the bridge gateway, which the L4 page explains.
Hostnames
Section titled “Hostnames”A rule can name a host instead of an address - a dynamic-DNS name for someone’s home connection, say. Caddy’s IP matchers only take ranges, so the controller does the lookup: it resolves A and AAAA records, writes the answer into the config, and looks the name up again whenever the record’s TTL runs out (clamped to between a minute and an hour), reloading Caddy only when the answer changed. A name is looked up as soon as it is saved, and the Network tab shows what each one currently stands for, or why it stands for nothing.
- IPv4 answers are exact; IPv6 answers cover their /64. A home connection’s IPv6 address moves
around inside its /64 as privacy addresses rotate, so one address would stop matching within
hours. End the name in a prefix length to change that:
home.example.com/56for a whole /56,home.example.com/128for the exact address. Anything wider than /48 is refused. - A failed lookup keeps the last answer for 24 hours, retried every minute. After that, and for a name that has never resolved, the name stands for no addresses at all.
- No addresses fails closed for allow, and open for deny. An allow rule with nothing behind it admits nobody; a deny rule with nothing behind it denies nobody, and the Network tab warns about it. A list whose only rule is an unresolved allow shuts everyone out - on an L4 host too.
- A name counts as one rule towards a list’s limit of 500, and stands for at most 16 addresses. Lookups go to the servers under Settings → DNS → DNS Resolvers when those are enabled, and to the controller’s own resolver otherwise - so a name that only resolves inside a network needs a controller that can see that network’s DNS.
A list can hold IP rules alone, users alone, or both. With both, Satisfy decides how they combine:
- All (the default): the address has to be allowed and the password right.
- Any: an allowed address gets straight in, and everyone else - a denied address included - is asked for the password. The office network walks in; working from home means logging in.
Per-path lists
Section titled “Per-path lists”A location rule can use a list of its own instead of the host’s,
or none at all: keep /admin/* to a stricter list, or leave /.well-known/* open on an otherwise
protected host.
Deleting a list
Section titled “Deleting a list”A list a host still uses cannot be deleted - whether the host itself, a location rule on it, an L4 host or the dashboard host uses it. The page names the hosts, and the REST API answers 409. Choose another list on them first, so nothing that was protected quietly stops being so.
Passing the password on
Section titled “Passing the password on”By default the upstream never sees the credentials a list checked: CPM removes the Authorization
header before forwarding the request. Turn on Pass auth to host for an application that reads
the username from it itself. Lists made before this setting existed have it on, which is how they
always behaved.
Simple, and appropriate for exactly the cases basic auth is appropriate for: an internal tool, a staging site, something you want off the open internet without standing up an identity provider.
Mutual TLS
Section titled “Mutual TLS”mTLS asks the client for a certificate. A visitor without one does not get a connection at all, which is a stronger boundary than any password - there is nothing to phish and nothing to guess.
CPM includes a CA for this. Issue client certificates from the Certificates page, hand them to the people or machines that need them, and enable mTLS on the hosts they should reach. The CA’s private key is encrypted at rest like every other secret; Caddy only ever gets the CA certificate.
Revocation is fail-closed. Revoking a certificate rejects it immediately, and revoking every certificate rejects every connection rather than falling open to none-required.
mTLS RBAC
Section titled “mTLS RBAC”Certificates can carry roles, and roles can be given path rules per host. So one certificate reaches
the application while another also reaches /admin/*:
| Path | Requires |
|---|---|
/* |
Any valid client certificate |
/admin/* |
The ops role |
Paths can also be excluded, which is how a health check endpoint or an ACME challenge path stays reachable without a certificate.
Roles are assigned to issued certificates, so revoking one certificate does not disturb the others holding the same role.
- opsReaches /admin/*1 cert
- staff1 cert
- avery@laptopexpires Jan 4, 2027
- backup-runnerexpires Jan 4, 2027