Here is a short recipe for wiring your Caddy so that all HTTP/HTTPS traffic outside Cloudflare is blocked.
- The main use case is to ensure that no one performs Denial of Service attack by going around Cloudflare, or any other security reverse proxy provider you might be using.
- Similar functionality can be achieved with
real_ip
and other plugins. This recipe focuses on simplicity, as it works without plugin installations. - Cloudflare IP ranges are not static: however they barely ever change, so updating them can be a manual job.
- Note that someone can still use Cloudflare Workers as a third party and make requests within Cloudflare IP range; however using this to large scale denial of service is very difficult.
First we create a shell script that prints out us a Cloudflare IPv4 / IPv6 line for Caddyfile
cloudflare-ip-list.sh
:
#!/bin/sh
#
# Prints out the latest Cloudflare IP list.
#
# Note: Third party Cloudflare workers can still access your site even if you
# block traffic from this range.
#
for i in `curl -s https://www.cloudflare.com/ips-v4`; do echo -n "$i "; done
for i in `curl -s https://www.cloudflare.com/ips-v6`; do echo -n "$i "; done
echo
Then we prepare Caddyfile
that uses the given IP range list and snippets. The example if for setting up multiple subdomains that repeat the rule:
#
# Caddy configuration example that blocks non-Cloudflare traffic
#
{
admin off
email no-reply@example.com
log {
output file /var/log/caddy/access.log
format json
}
}
#
# Caddy snippet to be used with subdomains to block all traffic
# that is not originated from Cloudflare.
#
# This is designed to block harmful knocking and IP scanning bot traffic.
#
# # Generate remote_ip list with cloudflare-ip-list.sh
#
# More about snippet features here
# - https://caddyserver.com/docs/caddyfile/concepts#snippets
# - https://caddy.community/t/caddy-v2-reusable-snippets/6744
#
(cloudflare-only) {
# Update IPs here from the shell script output
@blocked not remote_ip 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
respond @blocked "<h1>HTTP/HTTPS access through Cloudflare only</h1>" 403
}
# Specify a subdomain that can be only accessed through
# CloudFlare reverse proxy.
#
# To manually test using direct IP to see you are blocked:
#
# curl --header "Host: mysite.example.com" http://1.2.3.4
#
http://mysite.example.com {
handle {
import cloudflare-only
# Your website handling logic goes here
reverse_proxy 127.0.0.1:8080
}
}
:80 {
respond "<h1>HTTP request did not specify site domain</h1>" 403
}
:443 {
respond "<h1>HTTP request did not specify site domain</h1>" 403
}
Then you can test that direct IP access is blocked with curl:
curl --header "Host: mysite.example.com" http://1.2.3.4
<h1>HTTP/HTTPS access through Cloudflare only</h1>