Requirements
| WordPress | 5.0 or higher |
|---|---|
| PHP | 7.4 or higher |
| API Keys | None required. The plugin uses public geo-IP APIs out of the box. Optional MaxMind GeoLite2 database for faster, unlimited lookups |
| External Dependencies | None to install. Geo lookups use APIs (ipinfo.io, ip-api.com, ipapi.co, ipwho.is) with automatic failover |
Installation
- Download the ZIP file from your account
- Go to Plugins → Add New → Upload Plugin
- Upload the ZIP and click Install Now
- Click Activate
Uninstalling
Deactivate and delete from the Plugins screen. Scheduled cron jobs are removed on deactivation. Options stored under bsas_* in the wp_options table and the wp_bsas_logs table remain in your database. Remove these manually if you want a completely clean removal.
Quick Start
- Go to Country Blocker in your WordPress admin sidebar
- Make sure the Enable Blocking toggle is on (it is by default)
- Select countries, continents, or individual states/regions to block
- Click Save Settings
- Optionally configure email alerts, blocking schedules, and redirect rules on the same page
Visitors from the selected locations will immediately see a “403 Access Restricted” block page. You can customize the block page appearance in the Block Page tab.
Core Toggles
| Enable Blocking | Master on/off switch for the entire plugin. When off, no visitors are blocked regardless of other settings |
|---|---|
| Allow Search Engine Crawlers | When on, recognized search engine bots (Google, Bing, DuckDuckGo, Yandex, Baidu, Apple, and others) bypass all blocking rules. Recommended to keep on so your SEO is not affected |
| Strict VPN / Datacenter Detection | When on, the plugin checks the visitor's ISP/ASN against a list of known hosting and VPN providers. Visitors from recognized datacenter IPs are blocked even if their country is allowed |
| Block REST API | When on, blocking rules also apply to WordPress REST API requests (/wp-json/). By default the REST API is excluded from blocking |
Log Mode
| Log Everything | Records both blocked and allowed visitors (default) |
|---|---|
| Blocked Only | Only records visitors who were blocked |
| Allowed Only | Only records visitors who were allowed or bypassed |
| Disable Logging | No access logs are recorded. Reduces database writes for high-traffic sites |
Unknown Location Policy
| Allow (default) | If the plugin can't determine a visitor's country, they are allowed through |
|---|---|
| Block | If the plugin can't determine a visitor's country, they are blocked. Use this for strict compliance scenarios |
MaxMind GeoLite2 (Optional)
Enter your MaxMind license key to use a local database for geo lookups instead of external APIs. Faster, more accurate, and no rate limits. The database is auto-updated weekly. See the MaxMind Setup section for full instructions.
Countries & Continents
Country Blocking
A searchable grid of all 249 countries and territories. Use the search box to quickly find countries, or use the Select All / Deselect All buttons for bulk actions. Checked countries are blocked. The current count of blocked countries is shown above the grid.
Continent Blocking
Block entire continents with a single checkbox: Africa, Antarctica, Asia, Europe, North America, Oceania, and South America. Blocking a continent blocks all countries within it. This stacks with individual country selections.
State / Region Blocking
Block visitors at a sub-country level. State and region blocking is supported for seven countries:
| United States | All 50 states plus DC, Puerto Rico, Guam, and U.S. Virgin Islands |
|---|---|
| Canada | All 13 provinces and territories |
| United Kingdom | England, Scotland, Wales, and Northern Ireland |
| Australia | All 8 states and territories |
| Germany | All 16 Bundesländer |
| India | All 36 states and union territories |
| China | All 31 provinces, municipalities, and autonomous regions |
Email Alerts
| Enable Email Alerts | Toggle alert emails on/off |
|---|---|
| Email Address | Where to send alerts. Defaults to the WordPress admin email |
| Threshold | Number of blocked visitors required to trigger an alert. Default: 50 |
| Period | The time window for the threshold: Per Hour or Per Day |
When the number of blocked visitors in the configured period exceeds the threshold, the plugin sends a single alert email. A cooldown prevents duplicate emails — only one alert is sent per period.
Redirect Rules
Instead of showing the block page, redirect visitors from specific locations to a custom URL. Each rule has three fields:
| Type | What to match: Country, Continent, US State, CA Province, UK Region, AU State, DE State, IN State, or CN Province |
|---|---|
| Value | The specific location code to match (dropdown populated based on type) |
| URL | Where to redirect matching visitors (302 redirect) |
Click + Add Rule to create additional rules. Redirect rules are checked before the normal block page is shown. The first matching rule wins.
Blocking Schedules
Create time-based blocking profiles. Each schedule has:
| Enable toggle | Turn individual schedules on/off |
|---|---|
| Name | A label for your reference (e.g. “Business Hours”) |
| Days | Which days of the week the schedule is active |
| Start Hour | When blocking begins (24-hour format) |
| End Hour | When blocking ends (24-hour format). Supports overnight spans (e.g. 22:00 to 06:00) |
Blocking is active when any enabled schedule matches the current day and time. If no schedules exist or none are enabled, blocking is always active (24/7).
Page-Level Blocking
| Enable Page-Level Blocking | Toggle to restrict blocking to specific pages instead of the entire site |
|---|---|
| Page Selector | Dropdown of all published pages and posts. Add pages to the list by selecting and clicking + Add |
| Block ONLY these pages | Only the selected pages show the block page to restricted visitors. All other pages are accessible |
| Block all EXCEPT these pages | The entire site is blocked except the selected pages. Useful for keeping a landing page or legal notice accessible |
Auto-Block Repeat Offenders
| Enable Auto-Block | Toggle automatic IP blacklisting on/off |
|---|---|
| Threshold | Number of blocked visits before the IP hash is added to the blacklist. Default: 10 |
| Period | Time window: Per Hour or Per Day |
When an IP hash exceeds the threshold within the period, it's automatically added to the IP blacklist as a comment entry with a timestamp. Auto-blocked entries are processed during the daily maintenance cron job.
WooCommerce Integration
| Prevent Checkout from Blocked Regions | Visitors from blocked countries/regions are redirected back to the cart with an error message when they attempt checkout |
|---|---|
| Block Cart Access | Visitors from blocked countries/regions see the block page when they try to access the WooCommerce cart page |
IP Lists Tab
IP Whitelist
Enter IP addresses or CIDR ranges (one per line) that should always bypass blocking, regardless of country. Whitelisted visitors are logged with a “bypassed” decision and receive a 30-minute bypass cookie.
IP Blacklist
Enter IP addresses or CIDR ranges (one per line) that should always be blocked, regardless of country. Blacklisted visitors see the block page immediately without any geo lookup.
Both fields support IPv4 and IPv6, individual IPs and CIDR notation (e.g. 192.168.1.0/24, 2001:db8::/32). Lines starting with # are treated as comments and ignored.
Block Page Tab
Redirect (Optional)
Enter a URL to redirect all blocked visitors to instead of showing the block page. If set, the block page design settings below are ignored.
Block Page Colors
| Background | Page background color. Default: #1a1a2e (dark navy) |
|---|---|
| Text | Text color. Default: #eeeeee (light gray) |
| Accent | Icon gradient and accent color. Default: #7c3aed (purple) |
Block Page Content
| Heading | Main title on the block page. Default: “Access Restricted” |
|---|---|
| Message Body | Description text below the heading. Use {COUNTRY} as a placeholder for the visitor's detected country code. Default: “Access from {COUNTRY} is restricted.” |
Custom CSS
Add custom CSS to further style the block page. Available classes: .bsas-block-body, .bsas-panel, .bsas-icon, h1, p.
Live Preview
A real-time preview at the bottom of the tab shows exactly how your block page will look as you adjust colors, heading, and message text.
Logs Tab
A paginated table of all access log entries (50 per page), newest first. Each entry shows:
| ID | Auto-incrementing log entry number |
|---|---|
| Location | Two-letter country code (e.g. “US”, “DE”) |
| Region | State/region code if detected (e.g. “MI” for Michigan, “EN” for England) |
| Decision | Blocked (red), Allowed (green), Bypassed (blue), or Error (yellow) |
| Reason | Why the decision was made (e.g. “country_blocked”, “region_blocked”, “whitelisted”, “vpn_datacenter”, “redirect_rule”) |
| URI | The page the visitor tried to access |
| Time | Timestamp of the access |
Log Actions
| Export CSV | Downloads all log entries as a CSV file |
|---|---|
| Clear | Deletes all log entries from the database |
Log Retention Settings
| Retention Days | How many days to keep log entries. Default: 30. Set to 0 to keep logs forever. A daily maintenance cron job automatically removes older entries |
|---|---|
| Max Entries | Maximum number of log entries to keep. Default: 0 (unlimited). When exceeded, the oldest entries are deleted first |
How Blocking Works
When a visitor loads any frontend page, the plugin runs checks in this order:
- Admin, cron, AJAX? — If the request is to the admin area, a WP-CLI command, a cron job, or an AJAX request, blocking is skipped entirely
- Plugin enabled? — If the master toggle is off, all visitors are allowed
- REST API excluded? — If the request is to
/wp-json/and Block REST API is off, it's allowed - Cookie check — If the visitor has a valid bypass cookie from a previous allowed check, they pass through immediately (cookie lasts 30 minutes)
- IP whitelist — Whitelisted IPs are allowed immediately and logged as “bypassed”
- IP blacklist — Blacklisted IPs are blocked immediately with no geo lookup
- Crawler check — Recognized search engine bots are allowed if the crawler setting is on
- Schedule check — If no active schedule matches the current day and time, blocking is paused
- Geo lookup — The visitor's country and region are determined from cached transients (24-hour TTL) or by querying the geo APIs
- Unknown location — If the country can't be determined, the unknown location policy applies (allow or block)
- Redirect rules — If a redirect rule matches the visitor's location, they are 302 redirected to the configured URL
- Country / continent check — If the visitor's country or continent is in the blocked list, they are blocked
- Region check — If the visitor's state/region is in the blocked list, they are blocked
- VPN / datacenter check — If strict ASN detection is on and the visitor's ISP matches known hosting providers, they are blocked
- Page-level check — If page-level blocking is enabled, checks whether this specific page is targeted
- Allowed — A bypass cookie is set for 30 minutes so subsequent page loads skip the entire process
Geo Lookup
The plugin determines a visitor's country and region using a cascading fallback system:
- MaxMind local database — If you've entered a MaxMind license key and the GeoLite2-City.mmdb file exists, this is used first. Returns both country and region. Fastest and most reliable
- ipinfo.io — Returns country and region name
- ip-api.com — Returns country code and region code
- ipapi.co — Returns country code and region code
- ipwho.is — Returns country code and region
Each API has a circuit breaker: after 5 consecutive failures, that API is skipped for 15 minutes before being retried. This prevents slow or broken APIs from dragging down your site.
MaxMind Setup
- Sign up for an account at maxmind.com
- Log in and go to Manage License Keys
- Generate a new license key
- Paste the key into the MaxMind License Key field on the Control tab
- Click Save Settings
The plugin will download the GeoLite2-City database to /wp-content/uploads/GeoLite2-City.mmdb and update it automatically every week.
Privacy & Data
IP Hashing
By default, visitor IP addresses are stored as SHA-256 hashes using your WordPress AUTH_SALT as the hash key. The original IP is never stored unless you explicitly enable plain IP storage. This helps with GDPR and privacy compliance.
What's Stored
wp_bsas_logs | Database table containing access logs: hashed IP, optional plain IP, country code, region code, decision, reason, request URI, and timestamp |
|---|---|
bsas_* options | All plugin settings stored in the wp_options table |
| Transients | Geo lookup results cached for 24 hours per visitor IP hash |
| Cookie | A small cookie (BSASV) set for 30 minutes on allowed visitors to avoid repeated geo lookups |
External Connections
The plugin connects to geo-IP APIs (ipinfo.io, ip-api.com, ipapi.co, ipwho.is) to look up visitor locations. If you configure MaxMind, lookups happen locally with no external connections. No personal data is sent to third parties beyond the visitor's IP address for geo lookup purposes.
Performance
- Geo lookups are cached in transients for 24 hours per IP — repeat visitors cause zero API calls
- Allowed visitors receive a 30-minute bypass cookie, so subsequent page loads skip the entire blocking check
- The plugin uses a circuit breaker pattern: failed APIs are automatically skipped for 15 minutes
- All API requests have a 2-second timeout to prevent slowdowns if an API is unresponsive
- Admin pages, cron jobs, AJAX requests, and the REST API (by default) are always excluded from blocking checks
- Database log cleanup runs daily via WordPress cron, respecting both retention days and max entry limits
- Database query results are cached via
wp_cachefor log totals and statistics
Troubleshooting
Visitors from blocked countries are getting through
- Verify the Enable Blocking toggle is on in the Control tab
- Check that the specific country, its continent, or the visitor's region is in the blocked list
- The visitor may be using a VPN. Enable Strict VPN / Datacenter Detection to catch some VPN users
- If using schedules, verify the current day and time fall within an active schedule
- If using page-level blocking, verify the page is targeted
- Cached transients from before you made changes may still be active. They expire within 24 hours
- If the visitor has an allowed bypass cookie, they won't be rechecked for 30 minutes
- Admin users browsing the frontend are never blocked
I'm blocking myself
- The plugin never blocks admin area requests — you can always access
/wp-admin/to change settings - Add your IP to the IP Whitelist on the IP Lists tab
- If locked out of the frontend entirely, connect via FTP or your hosting file manager and rename the plugin folder to deactivate it
Region/state blocking isn't working
- Region data depends on the geo lookup returning subdivision information. The external APIs don't always include this
- Set up MaxMind GeoLite2 for the most reliable region data
- Check the Logs tab — look at the Region column to see if region codes are being detected for visitors
- Some mobile carriers and satellite ISPs may not resolve to accurate regions
Geo lookups seem inaccurate
- The external APIs have varying accuracy, especially for mobile and satellite internet users
- Set up MaxMind GeoLite2 for significantly better accuracy
- If your site is behind Cloudflare or a CDN, the CDN should be sending the real visitor IP via headers like
CF-Connecting-IPorX-Real-IP. The plugin checks these headers automatically
Email alerts not being received
- Verify Enable Email Alerts is on in the Control tab
- Check that the email address is correct
- Verify your WordPress site can send emails (install WP Mail SMTP if needed)
- Check spam/junk folders
- Alerts only fire when the blocked count exceeds the threshold within the period. Check the Logs tab to see if enough blocks are occurring
WooCommerce checkout blocking not working
- Verify Prevent Checkout from Blocked Regions is enabled on the Control tab
- The visitor's country or region must be in the blocked list for checkout blocking to apply
- Whitelisted IPs bypass all WooCommerce checks
- The visitor needs a geo lookup result — ensure APIs or MaxMind are working
The logs table is getting very large
- Reduce the Retention Days setting (default is 30)
- Set a Max Entries limit (e.g. 10,000)
- Switch Log Mode to Blocked Only or Disable Logging
- The daily maintenance cron job handles cleanup automatically
Scheduled scans aren't running on time
- WordPress cron depends on site traffic. If your site gets very little traffic, cron jobs may not fire on time
- Consider setting up a real server cron job that hits
wp-cron.phpon a schedule - Check that no other plugin or server config is disabling
wp-cron.php(look forDISABLE_WP_CRONin wp-config.php)