Many web applications face persistent abuse from specific geographic regions. In my case, Philippine IP ranges were generating:
- Carding attempts (testing stolen credit cards)
- Spam account registrations
- Automated vulnerability scanning
- Referral spam from .ph domains
While security measures like rate limiting and CAPTCHAs help, they don't address the root issue when:
- 90%+ of malicious traffic originates from one country
- Legitimate users from that region are non-existent
- The traffic consumes significant server resources
Here are three practical approaches to implement country blocking:
Option 1: MaxMind GeoIP with PHP (Recommended)
The most accurate solution using MaxMind's free GeoLite2 database:
// Download the GeoLite2 database from:
// https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
require_once 'vendor/autoload.php';
use MaxMind\Db\Reader;
$ip = $_SERVER['REMOTE_ADDR'];
$reader = new Reader('/path/to/GeoLite2-Country.mmdb');
try {
$record = $reader->get($ip);
if ($record && $record['country']['iso_code'] === 'PH') {
header('HTTP/1.1 403 Forbidden');
exit('Access from your region is not permitted');
}
} finally {
$reader->close();
}
Option 2: Apache .htaccess Blocking
For simpler implementations using IP ranges:
# Download Philippine IP ranges from:
# https://www.ipdeny.com/ipblocks/
# Add to your .htaccess
Order Allow,Deny
Allow from all
Deny from 1.0.0.0/21
Deny from 1.20.0.0/16
# ... (add all PH IP ranges)
Option 3: Cloudflare Firewall Rules
If using Cloudflare, create a firewall rule:
# In Cloudflare Dashboard:
# Security → WAF → Firewall Rules
# Create rule with expression:
# (ip.geoip.country eq "PH")
# Action: Block
When implementing country blocking:
- MaxMind DB adds ~0.5ms lookup time (negligible for most apps)
- .htaccess becomes slow with 500+ IP ranges
- Cloudflare adds zero server overhead
- Update GeoIP databases monthly (they change constantly)
- Monitor false positives in server logs
- Consider whitelisting known-good IPs that might be misclassified
Like many web developers, I've noticed an alarming pattern - while legitimate users from the Philippines are virtually nonexistent on my platform, malicious traffic from PH IP ranges continues to flood my servers. Common attack patterns include:
- Card testing attempts (200-300 requests/minute)
- Credential stuffing attacks
- Google.ph referrals leading to scraping bots
While blanket country blocking isn't ideal, the signal-to-noise ratio from Philippine IPs makes this a justified exception:
// Sample log analysis showing PH traffic patterns
2023-11-15 03:22:11 | 112.206.xxx.xxx (PH) | POST /api/payment | 403 Forbidden
2023-11-15 03:22:12 | 120.28.xxx.xxx (PH) | GET /login | 401 Unauthorized
2023-11-15 03:22:13 | 110.54.xxx.xxx (PH) | POST /register | 400 Bad Request
Option 1: Apache .htaccess Method
The most performant solution for Apache users:
# Block Philippines IP ranges
<IfModule mod_geoip.c>
GeoIPEnable On
SetEnvIf GEOIP_COUNTRY_CODE PH BlockCountry
Deny from env=BlockCountry
</IfModule>
# Fallback using MaxMind data if GeoIP module unavailable
<IfModule !mod_geoip.c>
# PH IP ranges (updated Q4 2023)
Deny from 110.54.0.0/15
Deny from 112.201.0.0/16
Deny from 120.28.0.0/16
# Add other PH CIDR blocks as needed
</IfModule>
Option 2: PHP Middleware Approach
For more flexibility in PHP applications:
// geo_block.php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;
$reader = new Reader('/usr/local/share/GeoIP/GeoLite2-Country.mmdb');
try {
$record = $reader->country($_SERVER['REMOTE_ADDR']);
if ($record->country->isoCode === 'PH') {
http_response_code(403);
exit('Access from your region is restricted');
}
} catch (Exception $e) {
// Fail open rather than block legitimate traffic
error_log("GeoIP error: ".$e->getMessage());
}
Keep your IP ranges current using these strategies:
- Subscribe to PH NIC updates (https://www.phnic.net.ph/)
- Set up a monthly cron job to refresh MaxMind databases
- Monitor new ASNs allocated to Philippine providers
Example update script:
#!/bin/bash
wget -O /tmp/GeoLite2-Country.tar.gz "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=YOUR_KEY&suffix=tar.gz"
tar -xzf /tmp/GeoLite2-Country.tar.gz -C /tmp
mv /tmp/GeoLite2-Country_*/GeoLite2-Country.mmdb /usr/local/share/GeoIP/
For those wanting more nuanced control:
// Hybrid approach combining GeoIP with rate limiting
$redis = new Redis();
$key = "ph_ip:".$_SERVER['REMOTE_ADDR'];
if ($redis->incr($key) > 10) {
header('HTTP/1.1 429 Too Many Requests');
exit;
}
$redis->expire($key, 3600);