Caddy
bash
# log location
/var/lib/caddy/.local/share/caddyForward Auth
https://docs.goauthentik.io/docs/providers/proxy/server_caddy
Update config via API
bash
caddy fmt --overwrite Caddyfile
curl localhost:2019/load \
-H "Content-Type: text/caddyfile" \
--data-binary @CaddyfileCookbook
Static files
bash
subdomain.domain.tld {
root * /opt/files
route {
crowdsec
file_server browse
# ---- alternate config ----
# Serve pre-compressed files if present
file_server /downloads/* {
precompressed gzip zstd br
}
# ---- end alternate config ----
# ---- optional config ----
encode zstd gzip
# ---- end optional config ----
}
}Basic auth
bash
# create password hash
caddy hash-password --algorithm bcryptbash
subdomain.domain.tld {
basicauth * {
$USERNAME $PASSWORD_HASH
}
reverse_proxy 127.0.0.1:$PORT
}IP filter
bash
# https://gist.github.com/morph027/b771fb579c36ae550ebb2764581a1d0e
intranet.example.com {
@ipfilter {
not remote_ip 192.168.0.0/16
}
route @ipfilter {
# redirect
redir https://example.com/
# or respond
# respond "Access denied" 403 {
# close
# }
}
reverse_proxy / https://intranet.lan
}SPA
bash
:80 {
# https://caddy.community/t/how-to-serve-spa-applications-with-caddy-v2/8761/2
try_files {path} /
encode gzip
root * /app/dist/spa
file_server
}Shorthand
bash
(base) {
route {
crowdsec
reverse_proxy $IP:{args[0]}
}
}
subdomain.domain.tld {
import base $PORT
}Set header
bash
subdomain.domain.tld {
reverse_proxy $IP:$PORT {
header_up Host example.com
}
}L4
bash
{
crowdsec {
api_url http://127.0.0.1:8080
api_key xxxxxxxxxxx
ticker_interval 15s
}
servers {
listener_wrappers {
layer4 {
@ssh ssh
route @ssh {
proxy $IP:$PORT
}
route
}
tls
}
}
}Set transport
bash
subdomain.domain.tld {
route {
crowdsec
reverse_proxy $IP:$PORT {
transport http {
dial_timeout 5m
response_header_timeout 5m
read_timeout 5m
write_timeout 5m
tls_insecure_skip_verify # optional
}
}
}
}Return 403
bash
subdomain.domain.tld {
route {
crowdsec
respond / "Access Denied" 403
reverse_proxy $IP:$PORT
}
}Set certain routes behind basicauth
bash
subdomain.domain.tld {
@api_gateway path /api/* /gateway/*
handle @api_gateway {
basicauth * {
username passwordhash
}
route {
crowdsec
reverse_proxy $IP:$PORT
}
}
handle {
route {
...
}
}
}Set API key header
bash
subdomain.domain.tld {
route {
crowdsec
rewrite * /api/foo{uri} # accessing $DOMAIN/api/foo is changed to $DOMAIN/
@unauthorized {
not {
header X-API-Key $API_KEY
}
}
respond @unauthorized "Unauthorized: Missing or invalid API key." 401 {
close
}
reverse_proxy http://$IP:PORT
}
}CORs
bash
(cors) {
@cors_preflight method OPTIONS
@cors header Origin {args[0]}
handle @cors_preflight {
header Access-Control-Allow-Origin "{args[0]}"
header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, HEAD"
header Access-Control-Allow-Headers "Range,If-Match"
header Access-Control-Max-Age "3600"
respond "" 204
}
handle @cors {
header Access-Control-Allow-Origin "{args[0]}"
header Access-Control-Expose-Headers "ETag"
}
}
a.domain.tld {
root * /opt/foo
file_server
import cors https://b.domain.tld
}Caddy with Plugins
bash
sudo apt install golang-go -y
go install "golang.org/dl/go1.25.4@latest"
go1.25.4 download
go env -w GOTOOLCHAIN=go1.25.4
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
~/go/bin/xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/mholt/caddy-l4 \
--with github.com/caddyserver/transform-encoder \
--with github.com/hslatman/caddy-crowdsec-bouncer/http@main \
--with github.com/hslatman/caddy-crowdsec-bouncer/appsec@main \
--with github.com/hslatman/caddy-crowdsec-bouncer/layer4@main
sudo mv caddy /usr/bin/
sudo groupadd --system caddy
sudo useradd --system \
--gid caddy \
--create-home \
--home-dir /var/lib/caddy \
--shell /usr/sbin/nologin \
--comment "Caddy web server" \
caddy
sudo systemctl unmask --now caddy.service # in case you uninstalled apt's caddyPaste this in sudo vi /etc/systemd/system/caddy.service
toml
# caddy.service
#
# For using Caddy with a config file.
#
# Make sure the ExecStart and ExecReload commands are correct
# for your installation.
#
# See https://caddyserver.com/docs/install for instructions.
#
# WARNING: This service does not use the --resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddy's API to configure it, add the --resume flag to the
# `caddy run` command or use the caddy-api.service file instead.
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.targetStart service via
bash
sudo systemctl daemon-reload
sudo systemctl enable caddy
sudo systemctl start caddyCrowdsec
bash
curl -s https://install.crowdsec.net | sudo sh
sudo apt install crowdsec -y
sudo cscli bouncers add caddy
# test
sudo cscli decisions add -i $IP -d 1m
sudo cscli metrics show decisionsResources
- Public and internal caddy network setup
- Setup CORS in Caddy 2
- Error pages - 🚧 Pretty server's error pages in the docker image & git repository (for traefik, k8s, nginx and so on).