-
Notifications
You must be signed in to change notification settings - Fork 0
CloudFlare Setup
Rumen Damyanov edited this page Jul 31, 2025
·
1 revision
Complete guide for configuring CloudFlare to provide geolocation headers for the php-geolocation package.
- CloudFlare Requirements
- Enabling IP Geolocation
- Header Configuration
- DNS Configuration
- Testing CloudFlare Integration
- CloudFlare Workers (Advanced)
- Page Rules for Geolocation
- SSL and Security Settings
- Troubleshooting CloudFlare Issues
- CloudFlare account (Free tier sufficient)
- Domain pointed to CloudFlare nameservers
- Website traffic flowing through CloudFlare (proxied DNS records)
| Feature | Free | Pro | Business | Enterprise |
|---|---|---|---|---|
| IP Geolocation | ✅ | ✅ | ✅ | ✅ |
| CF-IPCountry Header | ✅ | ✅ | ✅ | ✅ |
| Workers (Advanced) | ✅ (Limited) | ✅ | ✅ | ✅ |
| Page Rules | ✅ (3 rules) | ✅ (20 rules) | ✅ (50 rules) | ✅ (125 rules) |
- Log in to your CloudFlare Dashboard
- Select your domain
- Navigate to Network tab
# In CloudFlare Dashboard -> Network tab
1. Scroll down to "IP Geolocation" section
2. Toggle "IP Geolocation" to ON
3. The setting should show as "Enabled"Important Notes:
- This feature is available on all plans (including Free)
- Changes may take up to 5 minutes to propagate
- The setting applies to all traffic through CloudFlare
CloudFlare Dashboard
├── Domain Selection
├── Network Tab
│ ├── HTTP/2 (Enabled)
│ ├── HTTP/3 (Enabled)
│ ├── 0-RTT Connection Resumption
│ ├── IPv6 Compatibility
│ ├── WebSockets (Enabled)
│ ├── Onion Routing
│ ├── Pseudo IPv4
│ └── IP Geolocation ← Enable this
│ └── [Toggle Switch] → ON
When IP Geolocation is enabled, CloudFlare adds these headers:
CF-IPCountry: US
CF-Ray: 123abc456def789-LAX
CF-Visitor: {"scheme":"https"}
CF-Connecting-IP: 192.168.1.100| Header | Description | Example | Always Present |
|---|---|---|---|
CF-IPCountry |
ISO 3166-1 alpha-2 country code |
US, GB, DE
|
✅ (when geolocation enabled) |
CF-Ray |
Unique request identifier | 123abc456def789-LAX |
✅ |
CF-Visitor |
Connection scheme info | {"scheme":"https"} |
✅ |
CF-Connecting-IP |
Original visitor IP | 192.168.1.100 |
✅ |
Create a simple PHP test file:
<?php
// cloudflare-test.php
header('Content-Type: application/json');
$headers = [
'CF-IPCountry' => $_SERVER['HTTP_CF_IPCOUNTRY'] ?? 'Not Set',
'CF-Ray' => $_SERVER['HTTP_CF_RAY'] ?? 'Not Set',
'CF-Visitor' => $_SERVER['HTTP_CF_VISITOR'] ?? 'Not Set',
'CF-Connecting-IP' => $_SERVER['HTTP_CF_CONNECTING_IP'] ?? 'Not Set',
'Real-IP' => $_SERVER['REMOTE_ADDR'] ?? 'Not Set',
'User-Agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Not Set'
];
echo json_encode([
'cloudflare_status' => isset($_SERVER['HTTP_CF_RAY']) ? 'Active' : 'Not Active',
'geolocation_enabled' => isset($_SERVER['HTTP_CF_IPCOUNTRY']) ? 'Yes' : 'No',
'detected_country' => $_SERVER['HTTP_CF_IPCOUNTRY'] ?? null,
'headers' => $headers,
'timestamp' => date('Y-m-d H:i:s T')
], JSON_PRETTY_PRINT);
?>Access this file in your browser: https://yourdomain.com/cloudflare-test.php
Expected output when properly configured:
{
"cloudflare_status": "Active",
"geolocation_enabled": "Yes",
"detected_country": "US",
"headers": {
"CF-IPCountry": "US",
"CF-Ray": "123abc456def789-LAX",
"CF-Visitor": "{\"scheme\":\"https\"}",
"CF-Connecting-IP": "203.0.113.1",
"Real-IP": "172.16.0.1",
"User-Agent": "Mozilla/5.0..."
},
"timestamp": "2025-07-30 14:30:00 UTC"
}For geolocation to work, your DNS records must be proxied (orange cloud):
# Correct Configuration (Proxied - Orange Cloud)
A @ 203.0.113.1 [🟠] Proxied
A www 203.0.113.1 [🟠] Proxied
CNAME api @ [🟠] Proxied
# Incorrect Configuration (DNS Only - Gray Cloud)
A @ 203.0.113.1 [⚫] DNS Only ← Won't work
A www 203.0.113.1 [⚫] DNS Only ← Won't work
# Main domain with geolocation
A @ 203.0.113.1 [🟠] Proxied ← Geolocation works
# API subdomain with geolocation
A api 203.0.113.1 [🟠] Proxied ← Geolocation works
# CDN subdomain without geolocation (for static assets)
A cdn 203.0.113.1 [⚫] DNS Only ← No geolocation needed
# Check if traffic flows through CloudFlare
dig yourdomain.com
# Should return CloudFlare IP addresses (not your origin server)
# Test with curl
curl -I https://yourdomain.com
# Should include CF-Ray header
# Test geolocation header
curl -H "CF-IPCountry: DE" https://yourdomain.com/cloudflare-test.phpCreate cloudflare-integration-test.php:
<?php
require_once 'vendor/autoload.php';
use Rumenx\Geolocation\Geolocation;
class CloudFlareIntegrationTest
{
private array $results = [];
public function runTests(): array
{
$this->testCloudFlarePresence();
$this->testGeolocationHeaders();
$this->testGeolocationPackage();
$this->testCountryDetection();
$this->testHeaderForwarding();
return $this->results;
}
private function testCloudFlarePresence(): void
{
$cfRay = $_SERVER['HTTP_CF_RAY'] ?? null;
$this->results['cloudflare_presence'] = [
'test' => 'CloudFlare Presence',
'status' => $cfRay ? 'PASS' : 'FAIL',
'cf_ray' => $cfRay,
'message' => $cfRay ? 'Traffic flowing through CloudFlare' : 'Not behind CloudFlare proxy'
];
}
private function testGeolocationHeaders(): void
{
$cfCountry = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? null;
$this->results['geolocation_headers'] = [
'test' => 'Geolocation Headers',
'status' => $cfCountry ? 'PASS' : 'FAIL',
'country_header' => $cfCountry,
'message' => $cfCountry ? 'CF-IPCountry header present' : 'CF-IPCountry header missing - check IP Geolocation setting'
];
}
private function testGeolocationPackage(): void
{
try {
$geo = new Geolocation();
$country = $geo->getCountryCode();
$language = $geo->getLanguage();
$this->results['package_integration'] = [
'test' => 'Package Integration',
'status' => 'PASS',
'detected_country' => $country,
'detected_language' => $language,
'message' => 'php-geolocation package working correctly'
];
} catch (Exception $e) {
$this->results['package_integration'] = [
'test' => 'Package Integration',
'status' => 'FAIL',
'error' => $e->getMessage(),
'message' => 'php-geolocation package error'
];
}
}
private function testCountryDetection(): void
{
$cfCountry = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? null;
if ($cfCountry) {
$geo = new Geolocation();
$detectedCountry = $geo->getCountryCode();
$match = $cfCountry === $detectedCountry;
$this->results['country_detection'] = [
'test' => 'Country Detection Accuracy',
'status' => $match ? 'PASS' : 'WARNING',
'cf_country' => $cfCountry,
'detected_country' => $detectedCountry,
'message' => $match ? 'Country detection matches CloudFlare' : 'Country detection mismatch'
];
} else {
$this->results['country_detection'] = [
'test' => 'Country Detection Accuracy',
'status' => 'SKIP',
'message' => 'No CloudFlare country header to compare'
];
}
}
private function testHeaderForwarding(): void
{
$expectedHeaders = [
'HTTP_CF_IPCOUNTRY',
'HTTP_CF_RAY',
'HTTP_CF_CONNECTING_IP',
'HTTP_CF_VISITOR'
];
$presentHeaders = [];
$missingHeaders = [];
foreach ($expectedHeaders as $header) {
if (isset($_SERVER[$header])) {
$presentHeaders[] = $header;
} else {
$missingHeaders[] = $header;
}
}
$allPresent = empty($missingHeaders);
$this->results['header_forwarding'] = [
'test' => 'Header Forwarding',
'status' => $allPresent ? 'PASS' : 'WARNING',
'present_headers' => $presentHeaders,
'missing_headers' => $missingHeaders,
'message' => $allPresent ? 'All CloudFlare headers forwarded' : 'Some headers missing'
];
}
public function generateReport(): string
{
$report = "=== CloudFlare Integration Test Report ===\n\n";
$report .= "Generated: " . date('Y-m-d H:i:s T') . "\n\n";
foreach ($this->results as $test) {
$status = $test['status'];
$icon = $status === 'PASS' ? '✅' : ($status === 'WARNING' ? '⚠️' : '❌');
$report .= "$icon {$test['test']}: {$status}\n";
$report .= " Message: {$test['message']}\n";
if (isset($test['cf_country'])) {
$report .= " CF Country: {$test['cf_country']}\n";
}
if (isset($test['detected_country'])) {
$report .= " Detected: {$test['detected_country']}\n";
}
if (isset($test['error'])) {
$report .= " Error: {$test['error']}\n";
}
$report .= "\n";
}
return $report;
}
}
// Run the test
$test = new CloudFlareIntegrationTest();
$results = $test->runTests();
// Display results
header('Content-Type: text/plain');
echo $test->generateReport();
// Also output as JSON for API consumption
if (isset($_GET['format']) && $_GET['format'] === 'json') {
header('Content-Type: application/json');
echo json_encode($results, JSON_PRETTY_PRINT);
}
?># Test from command line
curl -s "https://yourdomain.com/cloudflare-integration-test.php" | head -20
# Test with specific country header (CloudFlare simulation)
curl -H "CF-IPCountry: GB" "https://yourdomain.com/cloudflare-integration-test.php"
# Get JSON format
curl -s "https://yourdomain.com/cloudflare-integration-test.php?format=json" | jq .
# Test multiple endpoints
for url in yourdomain.com www.yourdomain.com api.yourdomain.com; do
echo "Testing $url..."
curl -sI "https://$url" | grep -E "(CF-Ray|CF-IPCountry)"
echo ""
doneCloudFlare Workers can enhance geolocation data:
// Enhanced Geolocation Worker
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// Get original response
const response = await fetch(request)
// Get geolocation data
const country = request.cf.country
const region = request.cf.region
const city = request.cf.city
const timezone = request.cf.timezone
const continent = request.cf.continent
const latitude = request.cf.latitude
const longitude = request.cf.longitude
// Create enhanced headers
const newHeaders = new Headers(response.headers)
// Add enhanced geolocation headers
newHeaders.set('CF-IPCountry', country || 'XX')
newHeaders.set('X-Geo-Country', country || 'XX')
newHeaders.set('X-Geo-Region', region || '')
newHeaders.set('X-Geo-City', city || '')
newHeaders.set('X-Geo-Timezone', timezone || '')
newHeaders.set('X-Geo-Continent', continent || '')
newHeaders.set('X-Geo-Coordinates', `${latitude || 0},${longitude || 0}`)
// Add currency based on country
const currency = getCurrencyForCountry(country)
newHeaders.set('X-Geo-Currency', currency)
// Add language suggestions
const languages = getLanguagesForCountry(country)
newHeaders.set('X-Geo-Languages', languages.join(','))
// Return response with enhanced headers
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
})
}
function getCurrencyForCountry(country) {
const currencies = {
'US': 'USD', 'CA': 'CAD', 'GB': 'GBP', 'DE': 'EUR', 'FR': 'EUR',
'ES': 'EUR', 'IT': 'EUR', 'NL': 'EUR', 'JP': 'JPY', 'AU': 'AUD',
'BR': 'BRL', 'IN': 'INR', 'CN': 'CNY', 'RU': 'RUB', 'KR': 'KRW'
}
// EU countries default to EUR
const euCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE']
if (euCountries.includes(country)) {
return 'EUR'
}
return currencies[country] || 'USD'
}
function getLanguagesForCountry(country) {
const languages = {
'US': ['en'], 'CA': ['en', 'fr'], 'GB': ['en'], 'AU': ['en'],
'DE': ['de'], 'AT': ['de'], 'CH': ['de', 'fr', 'it'],
'FR': ['fr'], 'BE': ['fr', 'nl'], 'ES': ['es'], 'IT': ['it'],
'NL': ['nl'], 'PT': ['pt'], 'BR': ['pt'], 'RU': ['ru'],
'JP': ['ja'], 'CN': ['zh'], 'KR': ['ko'], 'IN': ['hi', 'en']
}
return languages[country] || ['en']
}# CloudFlare Dashboard -> Workers -> Routes
Route Pattern: *yourdomain.com/*
Worker: enhanced-geolocation-worker
<?php
// Enhanced geolocation data from CloudFlare Workers
class EnhancedCloudFlareGeolocation extends Geolocation
{
public function getRegion(): string
{
return $_SERVER['HTTP_X_GEO_REGION'] ?? parent::getRegion();
}
public function getCity(): string
{
return $_SERVER['HTTP_X_GEO_CITY'] ?? parent::getCity();
}
public function getTimezone(): string
{
return $_SERVER['HTTP_X_GEO_TIMEZONE'] ?? $this->getDefaultTimezone();
}
public function getContinent(): string
{
return $_SERVER['HTTP_X_GEO_CONTINENT'] ?? '';
}
public function getCoordinates(): array
{
$coords = $_SERVER['HTTP_X_GEO_COORDINATES'] ?? '';
if ($coords) {
[$lat, $lng] = explode(',', $coords);
return ['latitude' => (float)$lat, 'longitude' => (float)$lng];
}
return ['latitude' => 0, 'longitude' => 0];
}
public function getCurrency(): string
{
return $_SERVER['HTTP_X_GEO_CURRENCY'] ?? 'USD';
}
public function getSuggestedLanguages(): array
{
$languages = $_SERVER['HTTP_X_GEO_LANGUAGES'] ?? '';
return $languages ? explode(',', $languages) : ['en'];
}
private function getDefaultTimezone(): string
{
$timezones = [
'US' => 'America/New_York', 'CA' => 'America/Toronto',
'GB' => 'Europe/London', 'DE' => 'Europe/Berlin',
'FR' => 'Europe/Paris', 'JP' => 'Asia/Tokyo'
];
return $timezones[$this->getCountryCode()] ?? 'UTC';
}
}
// Usage
$geo = new EnhancedCloudFlareGeolocation();
echo "Timezone: " . $geo->getTimezone() . "\n";
echo "Currency: " . $geo->getCurrency() . "\n";
echo "Coordinates: " . json_encode($geo->getCoordinates()) . "\n";# CloudFlare Dashboard -> Page Rules
# Rule 1: Redirect non-US traffic to international site
URL Pattern: *yourdomain.com/*
Settings:
- If Country ≠ US: Forwarding URL (Status Code: 302)
Destination: https://international.yourdomain.com/$1
# Rule 2: Cache settings by region
URL Pattern: *yourdomain.com/api/*
Settings:
- If Country = US: Cache Level = Standard
- If Country ≠ US: Cache Level = Aggressive
# Rule 3: Security settings for specific countries
URL Pattern: *yourdomain.com/admin/*
Settings:
- If Country ∉ [US, CA, GB]: Security Level = I'm Under Attack
# Geographic Load Balancing (Business+ plans)
Rule: Geographic Distribution
URL: *yourdomain.com/*
Settings:
- North America → us-east.yourdomain.com
- Europe → eu-west.yourdomain.com
- Asia Pacific → ap-southeast.yourdomain.com
# Language-based redirects
Rule: Auto Language Redirect
URL: yourdomain.com/
Settings:
- If Accept-Language contains 'fr' AND Country ∈ [FR, CA, BE]:
Redirect to /fr/
- If Accept-Language contains 'de' AND Country ∈ [DE, AT, CH]:
Redirect to /de/
# CloudFlare Dashboard -> SSL/TLS
SSL/TLS Encryption Mode: Full (strict)
Always Use HTTPS: On
HTTP Strict Transport Security (HSTS): Enabled
- Max Age Header: 6 months
- Include Subdomains: On
- Preload: On
<?php
// Secure geolocation handling
class SecureCloudFlareGeolocation extends Geolocation
{
private array $trustedProxies = [
// CloudFlare IP ranges (simplified - use full list in production)
'103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22',
'104.16.0.0/12', '108.162.192.0/18', '131.0.72.0/22',
'141.101.64.0/18', '162.158.0.0/15', '172.64.0.0/13',
'173.245.48.0/20', '188.114.96.0/20', '190.93.240.0/20',
'197.234.240.0/22', '198.41.128.0/17'
];
public function __construct(array $server = null, array $countryToLanguage = [], string $cookieName = 'language')
{
// Validate that request comes from CloudFlare
if (!$this->isFromCloudFlare()) {
throw new SecurityException('Request not from trusted CloudFlare proxy');
}
// Validate geolocation headers
$server = $this->validateHeaders($server ?: $_SERVER);
parent::__construct($server, $countryToLanguage, $cookieName);
}
private function isFromCloudFlare(): bool
{
$clientIP = $_SERVER['REMOTE_ADDR'] ?? '';
// Check if client IP is in CloudFlare ranges
foreach ($this->trustedProxies as $range) {
if ($this->ipInRange($clientIP, $range)) {
return true;
}
}
// Also check for CF-Ray header as backup
return isset($_SERVER['HTTP_CF_RAY']);
}
private function validateHeaders(array $server): array
{
$clean = [];
// Validate CF-IPCountry
if (isset($server['HTTP_CF_IPCOUNTRY'])) {
$country = strtoupper(trim($server['HTTP_CF_IPCOUNTRY']));
if (preg_match('/^[A-Z]{2}$/', $country)) {
$clean['HTTP_CF_IPCOUNTRY'] = $country;
}
}
// Validate other headers
$safeHeaders = [
'HTTP_CF_RAY', 'HTTP_CF_VISITOR', 'HTTP_CF_CONNECTING_IP',
'HTTP_ACCEPT_LANGUAGE', 'HTTP_USER_AGENT'
];
foreach ($safeHeaders as $header) {
if (isset($server[$header])) {
$clean[$header] = $this->sanitizeHeader($server[$header]);
}
}
return $clean;
}
private function sanitizeHeader(string $value): string
{
// Remove potentially dangerous characters
$clean = preg_replace('/[^\w\-.,;:\/\s]/', '', $value);
return substr(trim($clean), 0, 500);
}
private function ipInRange(string $ip, string $range): bool
{
[$subnet, $mask] = explode('/', $range);
return (ip2long($ip) & ~((1 << (32 - $mask)) - 1)) === ip2long($subnet);
}
}
class SecurityException extends Exception {}
// Usage
try {
$geo = new SecureCloudFlareGeolocation();
echo "Secure geolocation: " . $geo->getCountryCode();
} catch (SecurityException $e) {
// Handle untrusted request
error_log("Untrusted geolocation request: " . $e->getMessage());
$geo = Geolocation::simulate('US'); // Fallback
}Symptoms:
-
$_SERVER['HTTP_CF_IPCOUNTRY']is not set - php-geolocation always returns default country
Diagnosis:
# Check if behind CloudFlare
curl -I https://yourdomain.com | grep CF-Ray
# Check geolocation setting
# Visit CloudFlare Dashboard -> Network -> IP GeolocationSolutions:
- Ensure IP Geolocation is enabled in CloudFlare Dashboard
- Verify DNS records are proxied (orange cloud)
- Wait 5-10 minutes for changes to propagate
- Clear CloudFlare cache
Symptoms:
- Wrong country detected
- Country changes unexpectedly
Diagnosis:
<?php
// Debug country detection
echo "CF Country: " . ($_SERVER['HTTP_CF_IPCOUNTRY'] ?? 'Missing') . "\n";
echo "CF Ray: " . ($_SERVER['HTTP_CF_RAY'] ?? 'Missing') . "\n";
echo "Real IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'Missing') . "\n";
echo "Connecting IP: " . ($_SERVER['HTTP_CF_CONNECTING_IP'] ?? 'Missing') . "\n";
// Test from different locations
$testIPs = ['8.8.8.8', '1.1.1.1', '208.67.222.222'];
foreach ($testIPs as $ip) {
echo "IP $ip location: " . file_get_contents("http://ip-api.com/line/$ip?fields=country") . "\n";
}
?>Solutions:
- Check if using VPN/proxy
- Verify CloudFlare has correct IP geolocation data
- Use CloudFlare's IP lookup tool
- Consider using multiple geolocation sources
Symptoms:
- CloudFlare working but headers missing in PHP
- Headers visible in browser dev tools but not in
$_SERVER
Diagnosis:
<?php
// Check all HTTP headers
foreach ($_SERVER as $key => $value) {
if (strpos($key, 'HTTP_') === 0) {
echo "$key: $value\n";
}
}
// Check if headers are being stripped
phpinfo(INFO_VARIABLES);
?>Solutions:
- Check web server configuration (Apache/Nginx)
- Verify no proxy stripping headers
- Check if behind additional load balancer
- Ensure CloudFlare proxy is closest to visitor
Symptoms:
- No CloudFlare headers in local environment
- Cannot test geolocation locally
Solutions:
<?php
// Local development simulation
class LocalCloudFlareSimulator
{
public static function simulate(string $country = 'US'): void
{
if (!isset($_SERVER['HTTP_CF_IPCOUNTRY'])) {
$_SERVER['HTTP_CF_IPCOUNTRY'] = $country;
$_SERVER['HTTP_CF_RAY'] = 'local-' . time() . '-DEV';
$_SERVER['HTTP_CF_VISITOR'] = '{"scheme":"https"}';
$_SERVER['HTTP_CF_CONNECTING_IP'] = '127.0.0.1';
}
}
}
// Use in local development
if (in_array($_SERVER['HTTP_HOST'] ?? '', ['localhost', '127.0.0.1']) ||
strpos($_SERVER['HTTP_HOST'] ?? '', '.local') !== false) {
LocalCloudFlareSimulator::simulate('DE'); // Test with Germany
}
$geo = new Geolocation();
echo "Country: " . $geo->getCountryCode(); // Will show 'DE' locally
?><?php
// cloudflare-debug.php
class CloudFlareDebugger
{
public function generateDebugReport(): string
{
$report = "=== CloudFlare Debug Report ===\n";
$report .= "Generated: " . date('Y-m-d H:i:s T') . "\n\n";
// Check CloudFlare presence
$report .= "CloudFlare Status:\n";
$report .= "- Behind CloudFlare: " . (isset($_SERVER['HTTP_CF_RAY']) ? 'Yes' : 'No') . "\n";
$report .= "- CF-Ray: " . ($_SERVER['HTTP_CF_RAY'] ?? 'Missing') . "\n";
$report .= "- Geolocation Enabled: " . (isset($_SERVER['HTTP_CF_IPCOUNTRY']) ? 'Yes' : 'No') . "\n\n";
// Headers analysis
$report .= "CloudFlare Headers:\n";
$cfHeaders = ['HTTP_CF_IPCOUNTRY', 'HTTP_CF_RAY', 'HTTP_CF_VISITOR', 'HTTP_CF_CONNECTING_IP'];
foreach ($cfHeaders as $header) {
$value = $_SERVER[$header] ?? 'Missing';
$report .= "- $header: $value\n";
}
$report .= "\n";
// Network information
$report .= "Network Information:\n";
$report .= "- Server IP: " . ($_SERVER['SERVER_ADDR'] ?? 'Unknown') . "\n";
$report .= "- Remote IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'Unknown') . "\n";
$report .= "- Forwarded For: " . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'None') . "\n";
$report .= "- Real IP: " . ($_SERVER['HTTP_X_REAL_IP'] ?? 'None') . "\n\n";
// Test geolocation package
$report .= "Geolocation Package Test:\n";
try {
$geo = new \Rumenx\Geolocation\Geolocation();
$report .= "- Country: " . $geo->getCountryCode() . "\n";
$report .= "- Language: " . $geo->getLanguage() . "\n";
$report .= "- City: " . $geo->getCity() . "\n";
$report .= "- Region: " . $geo->getRegion() . "\n";
$report .= "- Status: Working\n";
} catch (Exception $e) {
$report .= "- Error: " . $e->getMessage() . "\n";
$report .= "- Status: Failed\n";
}
return $report;
}
public function checkDNSConfiguration(): array
{
$domain = $_SERVER['HTTP_HOST'] ?? 'localhost';
$results = [];
// Check DNS records
$records = dns_get_record($domain, DNS_A);
$results['dns_records'] = $records;
// Check if IPs are CloudFlare
$cfRanges = ['104.16.0.0/12', '162.158.0.0/15', '172.64.0.0/13', '173.245.48.0/20'];
$results['cloudflare_ips'] = [];
foreach ($records as $record) {
if (isset($record['ip'])) {
$isCloudFlare = false;
foreach ($cfRanges as $range) {
if ($this->ipInRange($record['ip'], $range)) {
$isCloudFlare = true;
break;
}
}
$results['cloudflare_ips'][] = [
'ip' => $record['ip'],
'is_cloudflare' => $isCloudFlare
];
}
}
return $results;
}
private function ipInRange(string $ip, string $range): bool
{
[$subnet, $mask] = explode('/', $range);
return (ip2long($ip) & ~((1 << (32 - $mask)) - 1)) === ip2long($subnet);
}
}
// Run debugger
$debugger = new CloudFlareDebugger();
// Output report
header('Content-Type: text/plain');
echo $debugger->generateDebugReport();
echo "\n=== DNS Configuration ===\n";
$dnsConfig = $debugger->checkDNSConfiguration();
print_r($dnsConfig);
?><?php
// Monitor CloudFlare geolocation performance
class CloudFlareMonitor
{
private string $logFile = '/var/log/cloudflare-geo.log';
public function logRequest(): void
{
$data = [
'timestamp' => microtime(true),
'country' => $_SERVER['HTTP_CF_IPCOUNTRY'] ?? null,
'cf_ray' => $_SERVER['HTTP_CF_RAY'] ?? null,
'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'url' => $_SERVER['REQUEST_URI'] ?? null
];
file_put_contents($this->logFile, json_encode($data) . "\n", FILE_APPEND | LOCK_EX);
}
public function getStats(int $hours = 24): array
{
$lines = file($this->logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$since = time() - ($hours * 3600);
$stats = ['total' => 0, 'countries' => [], 'missing_geo' => 0];
foreach ($lines as $line) {
$data = json_decode($line, true);
if ($data && $data['timestamp'] > $since) {
$stats['total']++;
if ($data['country']) {
$stats['countries'][$data['country']] = ($stats['countries'][$data['country']] ?? 0) + 1;
} else {
$stats['missing_geo']++;
}
}
}
return $stats;
}
}
// Usage
$monitor = new CloudFlareMonitor();
$monitor->logRequest();
// View stats
$stats = $monitor->getStats(24);
echo "Last 24 hours: " . $stats['total'] . " requests\n";
echo "Missing geo: " . $stats['missing_geo'] . " requests\n";
echo "Top countries: " . json_encode(array_slice($stats['countries'], 0, 5, true)) . "\n";
?>- 🏭 Production Best Practices - Production deployment and monitoring
- 🌍 Multi-language Websites - International applications
- 🔧 Troubleshooting - Common issues and solutions
- 📊 Analytics Integration - Track geolocation data
Previous: CodeIgniter Integration | Next: Production Best Practices