Skip to content

Commit 5fe2b8b

Browse files
committed
content: new note for caddy on-demand tls trick
1 parent 0697ad5 commit 5fe2b8b

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
title: "Caddy On-Demand TLS trick"
3+
date: 2026-04-14
4+
tags: [caddy-tls]
5+
---
6+
7+
Caddy offers many ways to issue TLS certificates. However, due to the nature of wildcard certificates, which are restricted to only be issued by DNS-01 challenges, caddy will require you to add a custom DNS Module for your provider.
8+
9+
In scenarios with just a few subdomains, which are dynamic or change over time, and thus a limited number of certificates, we can just trick caddy into using On-Demand TLS. It avoids touching the config in the future and manually adding new subdomains, but still allows for dynamic issuing of certificates, but only for a domain we actually own and use for this server.
10+
11+
## On-Demand TLS
12+
13+
```caddyfile
14+
{
15+
on_demand_tls {
16+
ask http://localhost:2020/ask
17+
}
18+
}
19+
20+
:443 {
21+
tls {
22+
on_demand
23+
}
24+
25+
@example host example.com *.example.com
26+
handle @example {
27+
respond "Hello World"
28+
}
29+
}
30+
```
31+
32+
This is a minimal example on how this can be used. The endpoint will be asked and depending on the return code, caddy will start issuing a certificate or not. This is a rather cool feature, that can be used to allow custom domains on a service, without requiring a specific configuration for each domain.
33+
34+
A feature, we can trick caddy into using for wildcard certificates and without any external tools or servers. No need for a minimal python server that implements the ask endpoint.
35+
36+
## The trick
37+
38+
```caddyfile
39+
{
40+
on_demand_tls {
41+
ask http://localhost:2020/ask
42+
}
43+
}
44+
45+
http://localhost:2020 {
46+
# Check if the domain is allowed
47+
@allowed_domains {
48+
expression {query.domain} == 'example.com' || {query.domain}.endsWith('.example.com')
49+
}
50+
51+
handle @allowed_domains {
52+
respond 200
53+
}
54+
55+
# Block everything else
56+
handle {
57+
respond 404
58+
}
59+
}
60+
61+
:443 {
62+
tls {
63+
on_demand
64+
}
65+
66+
# This is just an example to match an exact domain.
67+
@example host example.com
68+
handle @example {
69+
respond "Hello World"
70+
}
71+
72+
@example_wildcard host *.example.com
73+
handle @example_wildcard {
74+
respond "WILDCARD"
75+
}
76+
}
77+
```
78+
79+
## Conclusion
80+
81+
This is more a trick and hacked version of using this feature just to avoid the need of adding a DNS module to the binary and access credentials to the DNS provider. But especially in scenarios where you only have a few subdomains it is a lot nicer, than having to touch the config every time you add a new subdomain.
82+
83+
---
84+
85+
#### References
86+
87+
- [Caddy On-Demand TLS](https://caddyserver.com/docs/automatic-https#on-demand-tls)
88+
- [Caddy On-Demand TLS Options Documentation](https://caddyserver.com/docs/caddyfile/options#on-demand-tls)

0 commit comments

Comments
 (0)