Replace 'private_ranges' with Cloudflare's official published edge ranges (15 IPv4 + 7 IPv6 from cloudflare.com/ips-v4 and /ips-v6). This ensures Cf-Connecting-Ip / X-Forwarded-For are only honoured for requests that genuinely originate from Cloudflare, preventing client-IP header spoofing from any other source. Validated + fmt-clean and tested at runtime against the caddy-cf image: Caddy starts cleanly with all 22 ranges, serves HTTP 200 with gzip and security headers.
75 lines
2.4 KiB
Caddyfile
75 lines
2.4 KiB
Caddyfile
# Caddyfile for the blog behind Cloudflare.
|
|
#
|
|
# Topology: Cloudflare (terminates TLS) -> Caddy (HTTP :80) -> SWS (blog:80)
|
|
#
|
|
# Cloudflare handles the public HTTPS cert, so Caddy serves plain HTTP on :80
|
|
# and does NOT need the Cloudflare DNS-01 / automatic-HTTPS setup. Set the
|
|
# Cloudflare SSL/TLS mode to "Full" so CF->origin is encrypted at the network
|
|
# layer (e.g. over your tunnel/Netbird), even though this listener is HTTP.
|
|
#
|
|
# Copy this file to /etc/caddy/Caddyfile on the caddy_cf host and reload:
|
|
# docker exec caddy_cf caddy reload --config /etc/caddy/Caddyfile
|
|
# or restart the container.
|
|
{
|
|
# Trust only Cloudflare's edge IPs so real visitor IPs (Cf-Connecting-Ip /
|
|
# X-Forwarded-For) are honoured only when the request actually comes from
|
|
# Cloudflare. This prevents header spoofing from any other source.
|
|
#
|
|
# Ranges from https://www.cloudflare.com/ips-v4/ and /ips-v6/ .
|
|
# Keep in sync when Cloudflare updates them (rarely). If you rebuild
|
|
# caddy-cf with the caddy-dynamic-cloudflare / caddy-cloudflare-ip plugin,
|
|
# this static list can be replaced with `trusted_proxies cloudflare`.
|
|
servers {
|
|
trusted_proxies static \
|
|
173.245.48.0/20 \
|
|
103.21.244.0/22 \
|
|
103.22.200.0/22 \
|
|
103.31.4.0/22 \
|
|
141.101.64.0/18 \
|
|
108.162.192.0/18 \
|
|
190.93.240.0/20 \
|
|
188.114.96.0/20 \
|
|
197.234.240.0/22 \
|
|
198.41.128.0/17 \
|
|
162.158.0.0/15 \
|
|
104.16.0.0/13 \
|
|
104.24.0.0/14 \
|
|
172.64.0.0/13 \
|
|
131.0.72.0/22 \
|
|
2400:cb00::/32 \
|
|
2606:4700::/32 \
|
|
2803:f800::/32 \
|
|
2405:b500::/32 \
|
|
2405:8100::/32 \
|
|
2a06:98c0::/29 \
|
|
2c0f:f248::/32
|
|
client_ip_headers Cf-Connecting-Ip X-Forwarded-For
|
|
}
|
|
}
|
|
|
|
# Bind to the hostname on the HTTP port only. Because the address has an
|
|
# explicit ":80" scheme/port, Caddy will NOT try to provision a TLS cert.
|
|
http://blog.ahmza.com {
|
|
# THE responder: proxy every request to the blog (SWS) container.
|
|
# This is what was missing before (requests fell through to a NOP -> blank page).
|
|
reverse_proxy blog:80
|
|
|
|
# Compression happens here (SWS has it disabled on purpose).
|
|
encode zstd gzip
|
|
|
|
# Security headers (single source of truth; SWS has security-headers off).
|
|
header {
|
|
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
X-Content-Type-Options "nosniff"
|
|
X-Frame-Options "SAMEORIGIN"
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
# Drop Caddy's default Server header.
|
|
-Server
|
|
}
|
|
|
|
log {
|
|
output stdout
|
|
format console
|
|
}
|
|
}
|