Cloudflare block countries
Block specific countries via Cloudflare WAF Custom Rules using the Cloudflare API. Use when user wants to geo-block traffic, block countries in Cloudflare, set up WAF country rules, or mentions blocking regions. Handles both creating new rulesets and updating existing ones.From its SKILL.md
npx -y skills add oleg-koval/agent-skills --skill cloudflare-block-countriesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
5.3 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
Cloudflare Country Block via WAF Custom Rules
Block traffic from specific countries using Cloudflare WAF Custom Rules and the Cloudflare API.
Prerequisites
- Cloudflare API Token with
Zone:Rulesets:Editpermission - Zone ID for the target domain
curlandjqinstalled
Workflow
1. Get Zone IDs
curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones?account.id=YOUR_ACCOUNT_ID&status=active" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" | jq '.result[] | {name: .name, id: .id}'
2. Check Existing Custom Firewall Rulesets
curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/rulesets" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" | \
jq '.result[] | select(.phase == "http_request_firewall_custom")'
If the output is empty — no existing ruleset. Go to 3a. If a ruleset exists, note its id and go to 3b.
3a. Create NEW Ruleset with Block Rule
curl -s -X POST \
"https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/rulesets" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"name": "default",
"description": "WAF Custom Rules",
"kind": "zone",
"phase": "http_request_firewall_custom",
"rules": [
{
"action": "block",
"description": "Block traffic from sanctioned countries",
"enabled": true,
"expression": "(ip.src.country in {\"RU\" \"BY\" \"IR\" \"KP\"})"
}
]
}' | jq '.success'
3b. Update EXISTING Ruleset
curl -s -X PUT \
"https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/rulesets/RULESET_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"description": "WAF Custom Rules",
"rules": [
{
"action": "block",
"description": "Block traffic from sanctioned countries",
"enabled": true,
"expression": "(ip.src.country in {\"RU\" \"BY\" \"IR\" \"KP\"})"
}
]
}' | jq '.success'
4. Verify the Rule is Active
curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/rulesets/RULESET_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" | jq '.result.rules'
Country Codes Reference
| Code | Country |
|---|---|
RU | Russia |
BY | Belarus |
IR | Iran |
KP | North Korea |
CN | China |
CU | Cuba |
SY | Syria |
VE | Venezuela |
WAF Expression Examples
# Block multiple countries
(ip.src.country in {"RU" "BY" "IR" "KP"})
# Block country + specific ASN
(ip.src.country eq "RU") or (ip.geoip.asnum eq 12345)
# Block all except allowlisted IPs
(ip.src.country in {"RU" "BY"}) and not (ip.src in {1.2.3.4/32})
Multi-Zone Script
To apply the same block rule across multiple zones:
#!/usr/bin/env bash
API_TOKEN="YOUR_API_TOKEN"
ZONES=("ZONE_ID_1" "ZONE_ID_2" "ZONE_ID_3")
EXPRESSION='(ip.src.country in {"RU" "BY" "IR" "KP"})'
for ZONE_ID in "${ZONES[@]}"; do
EXISTING=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" | \
jq -r '.result[] | select(.phase == "http_request_firewall_custom") | .id')
if [ -z "$EXISTING" ]; then
METHOD="POST"
URL="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets"
DATA=$(jq -n --arg expr "$EXPRESSION" '{
name: "default", description: "WAF Custom Rules",
kind: "zone", phase: "http_request_firewall_custom",
rules: [{action: "block", description: "Block sanctioned countries", enabled: true, expression: $expr}]
}')
else
METHOD="PUT"
URL="https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/$EXISTING"
DATA=$(jq -n --arg expr "$EXPRESSION" '{
description: "WAF Custom Rules",
rules: [{action: "block", description: "Block sanctioned countries", enabled: true, expression: $expr}]
}')
fi
RESULT=$(curl -s -X "$METHOD" "$URL" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
--data "$DATA" | jq '.success')
echo "Zone $ZONE_ID: $RESULT"
done
Notes
PUTon an existing ruleset replaces all rules in it. Include all rules in the payload if the ruleset already has other rules.- WAF Custom Rules require a Cloudflare plan that supports rulesets (Free includes basic WAF; Pro/Business/Enterprise for full custom rules).
- Changes are applied globally within seconds; no cache purge needed.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.