Skip to content

Instantly share code, notes, and snippets.

@Le0Developer
Last active April 21, 2025 02:29
Show Gist options
  • Save Le0Developer/4c68f9a878a4cc2db88755ae06191dbc to your computer and use it in GitHub Desktop.
Save Le0Developer/4c68f9a878a4cc2db88755ae06191dbc to your computer and use it in GitHub Desktop.
My cloudflare firewall rules

Short rule explanation

NOTE: These rules are for restricting use of your site to only humans using browsers. See the Using with section below to use these rules with webhooks/websockets/apis/tunnels etc.

  • 1-allow: allow definite bots + discord embed/image proxy
  • 2-block: block bots and unusual requests
  • 3-block: block HTTP/1.x
  • 4-challenge: challenge suspicious ASNs (will affect VPNs)

Using with a webhook

Webhooks will likely be blocked because they're requests made by bots. Add a very restrictive check to the 1-allow rule to allow only that one webhook to pass through.

Example:

or (
  (ip.geoip.asnum in {1234})
  and (http.user_agent eq "good webhook boi")
  and (http.request.full_uri eq "https://example.com/api/webhook/good-boi")
  and any(http.request.headers["authorization"][*] == "......."))
)

Using with a public api

If you have a public API (not used by browsers!), you will likely run into a lot of blocks. These firewall rules are designed to block all bots and they don't know about your API, so the best thing you can do is a check if the request is for your API and just... let it through.

You can add the following line to your 1-allow firewall rule.

or (http.host in {"public-api.example.com"})

Using with Wordpress

I can't and don't recommend using Wordpress.

If you absolutetly must use Wordpress, remove the following checks from the 2-block rule and secure your Wordpress site using Cloudflare Access:

or (lower(http.request.uri.path) contains "wp-")
or (lower(http.request.uri.path) contains ".php")
or (lower(http.request.uri.path) contains ".xml")

Using with TCP tunnels

The problem is cloudflared uses Go-http-client which is blocked by 2-block and uses HTTP/1.1.

The best solution is to put the entire host behind Cloudflare Access and then add the host in 1-allow.

or (http.host in {"tunnel.example.com"})

Disclaimer

These rules are provided on a best-effort basis.
Feel free to contact me (SoraDev#1337) in the Cloudflare Discord for feedback.

Licensed under MIT.

(cf.client.bot)
or (
(ip.geoip.asnum in {14618 15169 396982})
and (http.user_agent in {"Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)" "Mozilla/5.0 (Macintosh; Intel Mac OS X 11.6; rv:92.0) Gecko/20100101 Firefox/92.0"})
)
or (
(ip.geoip.asnum in {32575})
and (http.request.uri.path contains "/.well-known/pki-validation/")
)
not (http.user_agent contains "Mozilla/5.0 ")
or (lower(http.request.uri.path) contains "wp-")
or (lower(http.request.uri.path) contains ".php")
or (lower(http.request.uri.path) contains ".xml")
or (http.request.uri.path contains "/.")
or (lower(http.user_agent) contains "scaninfo@paloaltonetworks.com")
or (lower(http.user_agent) contains "netsystemsresearch.com")
or (lower(http.user_agent) contains "bot")
or (lower(http.user_agent) contains "headless")
or (not cf.edge.server_port in {80 443})
or (any(http.request.headers["host"][*] contains ":"))
or (
not (
any(http.request.headers["upgrade"][*] eq "websocket")
and (any(lower(http.request.headers.names[*])[*] == "sec-websocket-extensions"))
and (any(lower(http.request.headers.names[*])[*] == "sec-websocket-key"))
and (any(lower(http.request.headers.names[*])[*] == "sec-websocket-version"))
)
and (
not (any(lower(http.request.headers.names[*])[*] == "sec-fetch-site"))
and not (http.user_agent contains " Version/")
)
)
not (
any(http.request.headers["upgrade"][*] eq "websocket")
and (any(lower(http.request.headers.names[*])[*] == "sec-websocket-extensions"))
and (any(lower(http.request.headers.names[*])[*] == "sec-websocket-key"))
and (any(lower(http.request.headers.names[*])[*] == "sec-websocket-version"))
)
and (http.request.version in {"HTTP/1.0" "HTTP/1.1" "HTTP/1.2"})
(ip.geoip.asnum in {
4766 6461 6724 6939 8075 8560 8972 9009 9335 12025 12552 12816 12876 13213 14061 14576 14618 15169 16276 16509 17090 17252 18978 20278 20473 21859 22612 24187 24940 25369 26496 26548 27640 29182 29802 30083 30860 31898 32097 32613 32934 33182 34088 34549 34665 35779 35913 36351 36352 37963 38731 39351 39729 39845 40021 40676 41564 42675 42708 42831 43289 43350 43357 43624 44222 44901 45090 45382 45638 45899 46549 46606 47285 47810 47890 48090 48693 48716 48950 49063 49453 49505 51167 51430 51559 51747 51852 52423 53340 53667 54203 54538 55081 55720 55990 56655 56694 57172 57416 57509 57858 60068 60798 61003 61302 62240 62651 62744 63018 63199 63949 64280 131199 131392 132203 135330 135377 136557 136907 136787 140389 141039 141434 141995 147049 197226 197695 197706 198651 200019 200698 201094 201133 203020 203087 203999 204957 205119 206092 207137 207651 207713 208425 209103 209366 209588 209854 210106 210228 210352 210546 210644 210743 211138 211252 212238 213230 262287 396356 393886 396982 397391 398043 398101 398324 398722
})
@Butterkeks1000
Copy link

Cloudflare Browser SSH is still using HTTP 1.1 so this could break when you challenge / Block it

@Le0Developer
Copy link
Author

Cloudflare Browser SSH is still using HTTP 1.1 so this could break when you challenge / Block it

It's in the readme @Butterkeks1000.

Cloudflare firewall rules I use.
If you are using tunnels for SSH, you'll need to add the domains as exception in the first rule (should be safe because you should always put those behind access)

@Butterkeks1000
Copy link

Ah didnt see it, thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment