Files
Linux-Server-Management-Too…/modules/website/cloudflare-detector.sh
T
cschantz c962fe56e7 Add Cloudflare Domain Detector with datacenter location
Features:
- Scan all domains on server for Cloudflare usage
- Check single domain with detailed analysis
- Detects Cloudflare via: nameservers, IP ranges, HTTP headers
- Shows Cloudflare datacenter location (IATA code from CF-RAY)
- Useful for debugging regional outages and cache issues

Detection Methods:
1. Nameserver check (*.cloudflare.com)
2. IP address check (Cloudflare IP ranges)
3. HTTP header check (CF-RAY, Server: cloudflare)
4. Datacenter location extraction (e.g., ORD, LAX, LHR)

Output shows:
- Domains using Cloudflare [with datacenter code]
- Domains NOT using Cloudflare
- Unknown/uncertain domains

Integrated into Website Diagnostics Menu (option 4)

Example output:
  ✓ pickledperil.com                                [BNA]
  • example.com
2026-01-27 17:37:55 -05:00

529 lines
16 KiB
Bash
Executable File

#!/bin/bash
################################################################################
# Cloudflare Detector - Identify domains using Cloudflare
################################################################################
# Scans all domains on the server and detects Cloudflare usage
# Checks: Nameservers, IP ranges, and HTTP headers
################################################################################
# Source common functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../lib/common-functions.sh" 2>/dev/null || {
echo "[ERROR] Cannot load common-functions.sh"
exit 1
}
# Detect control panel and system
if type -t detect_system &>/dev/null; then
detect_system
fi
################################################################################
# Configuration
################################################################################
# Cloudflare IP ranges (IPv4) - updated as of 2024
# Source: https://www.cloudflare.com/ips-v4
CLOUDFLARE_IPV4_RANGES=(
"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"
)
################################################################################
# Helper Functions
################################################################################
get_domains() {
local domains=()
# Get domains from cPanel userdata
if [ -d /var/cpanel/userdata ]; then
while IFS= read -r domain_file; do
local domain=$(basename "$domain_file")
# Skip cache files, main file, and config files
if [[ "$domain_file" =~ \.cache$ ]] || \
[[ "$domain_file" =~ /cache$ ]] || \
[[ "$domain_file" =~ /main$ ]] || \
[[ "$domain" =~ _SSL$ ]] || \
[[ "$domain" =~ \.yaml$ ]] || \
[[ "$domain" =~ \.json$ ]] || \
[[ "$domain" =~ \.conf$ ]] || \
[[ "$domain" =~ \.backup$ ]]; then
continue
fi
# Skip system/template domains
if [[ "$domain" =~ cloudvpstemplate\. ]] || \
[[ "$domain" =~ cprapid\. ]] || \
[[ "$domain" =~ ^[0-9\-]+\. ]] || \
[[ "$domain_file" =~ /nobody/ ]]; then
continue
fi
# Only add if it looks like a domain
if [[ "$domain" != "." && "$domain" != ".." && "$domain" =~ \. ]]; then
domains+=("$domain")
fi
done < <(find /var/cpanel/userdata -type f 2>/dev/null)
fi
printf '%s\n' "${domains[@]}" | sort -u
}
is_cloudflare_ip() {
local ip="$1"
# Convert IP to integer for range checking
local ip_int=$(echo "$ip" | awk -F. '{print ($1 * 16777216) + ($2 * 65536) + ($3 * 256) + $4}')
for range in "${CLOUDFLARE_IPV4_RANGES[@]}"; do
local network=$(echo "$range" | cut -d'/' -f1)
local cidr=$(echo "$range" | cut -d'/' -f2)
# Calculate network range
local net_int=$(echo "$network" | awk -F. '{print ($1 * 16777216) + ($2 * 65536) + ($3 * 256) + $4}')
local mask=$((0xFFFFFFFF << (32 - cidr)))
local start=$((net_int & mask))
local end=$((start + (1 << (32 - cidr)) - 1))
# Check if IP is in this range
if [ "$ip_int" -ge "$start" ] && [ "$ip_int" -le "$end" ]; then
return 0
fi
done
return 1
}
check_nameservers() {
local domain="$1"
# Get nameservers
local nameservers=$(dig +short NS "$domain" 2>/dev/null | tr '[:upper:]' '[:lower:]')
if [ -z "$nameservers" ]; then
echo "UNKNOWN"
return 2
fi
# Check if any nameserver contains cloudflare
if echo "$nameservers" | grep -q "cloudflare"; then
echo "CLOUDFLARE"
return 0
fi
echo "OTHER"
return 1
}
check_ip_address() {
local domain="$1"
# Get A record
local ip=$(dig +short A "$domain" 2>/dev/null | head -1)
if [ -z "$ip" ]; then
echo "UNKNOWN"
return 2
fi
# Check if IP is in Cloudflare range
if is_cloudflare_ip "$ip"; then
echo "CLOUDFLARE"
return 0
fi
echo "DIRECT"
return 1
}
check_http_headers() {
local domain="$1"
# Try HTTP request and look for Cloudflare headers
local headers=$(curl -sI -m 3 "http://$domain" 2>/dev/null)
if [ -z "$headers" ]; then
echo "UNKNOWN"
return 2
fi
# Check for CF-RAY header (definitive Cloudflare indicator)
if echo "$headers" | grep -qi "CF-RAY:"; then
echo "CLOUDFLARE"
return 0
fi
# Check for Server: cloudflare
if echo "$headers" | grep -qi "Server:.*cloudflare"; then
echo "CLOUDFLARE"
return 0
fi
echo "DIRECT"
return 1
}
get_cloudflare_location() {
local domain="$1"
# Get CF-RAY header which contains datacenter code
local cf_ray=$(curl -sI -m 3 "http://$domain" 2>/dev/null | grep -i "CF-RAY:" | head -1 | awk '{print $2}' | tr -d '\r')
if [ -z "$cf_ray" ]; then
echo "N/A"
return 1
fi
# Extract datacenter code (last 3 characters, e.g., "ORD" from "9c1b340e5cdacc3a-ORD")
local colo=$(echo "$cf_ray" | awk -F'-' '{print $NF}')
if [ -n "$colo" ]; then
echo "$colo"
return 0
fi
echo "N/A"
return 1
}
detect_cloudflare() {
local domain="$1"
local ns_result=$(check_nameservers "$domain")
local ip_result=$(check_ip_address "$domain")
local http_result=$(check_http_headers "$domain")
# Cloudflare is confirmed if ANY check returns CLOUDFLARE
if [ "$ns_result" = "CLOUDFLARE" ] || \
[ "$ip_result" = "CLOUDFLARE" ] || \
[ "$http_result" = "CLOUDFLARE" ]; then
echo "CLOUDFLARE"
return 0
fi
# If all checks say DIRECT, it's definitely not Cloudflare
if [ "$ns_result" = "OTHER" ] && \
[ "$ip_result" = "DIRECT" ] && \
[ "$http_result" = "DIRECT" ]; then
echo "DIRECT"
return 1
fi
# Otherwise, uncertain
echo "UNKNOWN"
return 2
}
################################################################################
# Main Functions
################################################################################
scan_all_domains() {
print_banner "Cloudflare Detection Scan"
echo "Scanning all domains on this server..."
echo "Checking: Nameservers, IP addresses, HTTP headers"
echo ""
# Get all domains
local domains=$(get_domains)
if [ -z "$domains" ]; then
print_error "No domains found on this server"
echo ""
echo "This tool requires cPanel with configured domains"
press_enter
return 1
fi
local domain_count=$(echo "$domains" | wc -l)
echo "Found $domain_count domains to check..."
echo ""
sleep 1
# Arrays to store results
local -a cloudflare_domains=()
local -a cloudflare_locations=()
local -a direct_domains=()
local -a unknown_domains=()
# Progress tracking
local current=0
while IFS= read -r domain; do
current=$((current + 1))
# Show progress
printf "\r[%d/%d] Checking: %-50s" "$current" "$domain_count" "$domain"
# Detect Cloudflare
local result=$(detect_cloudflare "$domain")
case "$result" in
"CLOUDFLARE")
cloudflare_domains+=("$domain")
# Get Cloudflare datacenter location
local location=$(get_cloudflare_location "$domain")
cloudflare_locations+=("$location")
;;
"DIRECT")
direct_domains+=("$domain")
;;
*)
unknown_domains+=("$domain")
;;
esac
done <<< "$domains"
echo ""
echo ""
# Display results
echo "═══════════════════════════════════════════════════════════════"
echo " SCAN RESULTS"
echo "═══════════════════════════════════════════════════════════════"
echo ""
# Cloudflare domains
if [ ${#cloudflare_domains[@]} -gt 0 ]; then
print_success "🔶 Domains using Cloudflare: ${#cloudflare_domains[@]}"
echo ""
for i in "${!cloudflare_domains[@]}"; do
local domain="${cloudflare_domains[$i]}"
local location="${cloudflare_locations[$i]}"
if [ "$location" != "N/A" ]; then
printf " ✓ %-50s [%s]\n" "$domain" "$location"
else
echo "$domain"
fi
done
echo ""
else
echo "🔶 Domains using Cloudflare: 0"
echo ""
fi
# Direct domains
if [ ${#direct_domains[@]} -gt 0 ]; then
print_info "🌐 Domains NOT using Cloudflare: ${#direct_domains[@]}"
echo ""
for domain in "${direct_domains[@]}"; do
echo "$domain"
done
echo ""
else
echo "🌐 Domains NOT using Cloudflare: 0"
echo ""
fi
# Unknown domains
if [ ${#unknown_domains[@]} -gt 0 ]; then
print_warning "❓ Uncertain (DNS/connectivity issues): ${#unknown_domains[@]}"
echo ""
for domain in "${unknown_domains[@]}"; do
echo " ? $domain"
done
echo ""
fi
echo "═══════════════════════════════════════════════════════════════"
echo ""
echo "Summary:"
echo " Total domains: $domain_count"
echo " Cloudflare: ${#cloudflare_domains[@]}"
echo " Direct: ${#direct_domains[@]}"
echo " Unknown: ${#unknown_domains[@]}"
echo ""
press_enter
}
check_single_domain() {
print_banner "Check Single Domain"
echo ""
read -p "Enter domain name to check: " domain
if [ -z "$domain" ]; then
print_error "No domain entered"
press_enter
return 1
fi
echo ""
echo "Checking: $domain"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Check nameservers
print_info "1. Nameserver Check:"
local ns_result=$(check_nameservers "$domain")
local nameservers=$(dig +short NS "$domain" 2>/dev/null)
echo " Nameservers:"
if [ -n "$nameservers" ]; then
echo "$nameservers" | sed 's/^/ /'
else
echo " (none found)"
fi
echo " Status: $ns_result"
echo ""
# Check IP address
print_info "2. IP Address Check:"
local ip=$(dig +short A "$domain" 2>/dev/null | head -1)
if [ -n "$ip" ]; then
echo " IP: $ip"
local ip_result=$(check_ip_address "$domain")
echo " Status: $ip_result"
else
echo " (no A record found)"
fi
echo ""
# Check HTTP headers
print_info "3. HTTP Header Check:"
echo " Testing: http://$domain"
local http_result=$(check_http_headers "$domain")
echo " Status: $http_result"
# Get location if using Cloudflare
if [ "$http_result" = "CLOUDFLARE" ]; then
local location=$(get_cloudflare_location "$domain")
echo " Datacenter: $location"
fi
echo ""
# Final result
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
local final_result=$(detect_cloudflare "$domain")
case "$final_result" in
"CLOUDFLARE")
local location=$(get_cloudflare_location "$domain")
if [ "$location" != "N/A" ]; then
print_success "$domain is using Cloudflare (Datacenter: $location)"
else
print_success "$domain is using Cloudflare"
fi
;;
"DIRECT")
print_info "$domain is NOT using Cloudflare"
;;
*)
print_warning "? Could not determine Cloudflare status for $domain"
;;
esac
echo ""
press_enter
}
show_info() {
print_banner "About Cloudflare Detection"
echo ""
echo "How Detection Works:"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "This tool checks three indicators to detect Cloudflare:"
echo ""
echo " 1. Nameservers:"
echo " Checks if domain uses Cloudflare nameservers"
echo " (e.g., ns1.cloudflare.com)"
echo ""
echo " 2. IP Address:"
echo " Checks if A record points to Cloudflare IP range"
echo " (e.g., 104.16.x.x, 172.64.x.x, etc.)"
echo ""
echo " 3. HTTP Headers:"
echo " Checks for CF-RAY header in HTTP response"
echo " (definitive proof of Cloudflare proxy)"
echo ""
echo " 4. Datacenter Location:"
echo " Extracts IATA airport code from CF-RAY header"
echo " (e.g., ORD=Chicago, LAX=Los Angeles, LHR=London)"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo "Why This Matters:"
echo ""
echo " • Cloudflare domains hide origin server IP"
echo " • Useful for cache debugging (Cloudflare vs Varnish)"
echo " • Important for firewall/security configuration"
echo " • Helps identify which domains get CDN benefits"
echo " • Datacenter location useful for debugging regional outages"
echo ""
echo "Common Datacenter Codes:"
echo " ORD - Chicago | LAX - Los Angeles | IAD - Virginia"
echo " DFW - Dallas | ATL - Atlanta | SEA - Seattle"
echo " LHR - London | FRA - Frankfurt | AMS - Amsterdam"
echo " SYD - Sydney | SIN - Singapore | NRT - Tokyo"
echo ""
press_enter
}
################################################################################
# Main Menu
################################################################################
show_menu() {
clear
print_banner "Cloudflare Domain Detector"
echo ""
echo -e "${BOLD}Scan Options:${NC}"
echo ""
echo " 1) Scan All Domains - Check all domains on this server"
echo " 2) Check Single Domain - Detailed check for one domain"
echo " 3) About / How It Works - Detection methodology"
echo ""
echo " 0) Back to Website Menu"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -n "Select option: "
}
main() {
while true; do
show_menu
read -r choice
case $choice in
1) scan_all_domains ;;
2) check_single_domain ;;
3) show_info ;;
0)
clear
exit 0
;;
*)
echo ""
print_error "Invalid option"
sleep 1
;;
esac
done
}
# Run main menu
main