Geo-IP tracking
Burst determines visitor location from their IP address using edge/CDN country headers where available, falling back to the MaxMind GeoLite2 database. Country-level location tracking ships in the free plugin; Burst Pro extends it with region and city detail. No data is sent to external services at tracking time — lookups happen entirely on your server using request headers and a locally stored database file.
Pro - CreatorAvailable in the Creator tier
Region and city level detail requires Burst Pro. The free plugin tracks visitors at the country level. Drill down from country to state, region and city level
How it works
When a visit is recorded, Burst resolves the visitor's location on the first hit of the session.
Resolution follows a fixed order:
- Edge/CDN country — if the request carries a country header set by a CDN (Cloudflare, Vercel, CloudFront, Fastly, Netlify, nginx), that country is used. It is computed by the edge from live geodata, so it is fresher than the local database and needs no license key. The country is enriched with a continent code so the continent stats view keeps working.
- Local MaxMind database — when no edge country is present, Burst resolves the IP against the local database file. The free plugin reads the GeoLite2 Country database and stores the country (and continent). Pro reads the richer GeoLite2 City database and additionally stores the state/region and city.
The free Country reader treats the edge country as authoritative and short-circuits the database lookup. The Pro City reader keeps priority — because the City database carries the richer country + region + city record — and uses the edge country only as a fallback when no local city record is available (missing City database or an IP that is not in it).
The reader is resolved at tracking time through the burst_geoip_handler filter, so the free build always uses the country reader and Pro swaps in the city reader without any configuration. The database file is downloaded automatically on plugin activation or upgrade and refreshed on the burst_daily cron job.
Loopback requests (127.0.0.1 and ::1) have no resolvable location, so Burst returns filler location data for them. This happens whether or not a database file is loaded, which keeps location fields populated on local development installs. Use the burst_localhost_location_data filter to change the values.
Edge and CDN country detection
If your site sits behind a CDN or edge proxy, the visitor country is often already resolved by the edge and attached to the request as an ISO 3166-1 alpha-2 code on a request header. Burst reads the first of the following $_SERVER keys that is present:
| Provider | Header |
|---|---|
| Cloudflare | HTTP_CF_IPCOUNTRY |
| Vercel | HTTP_X_VERCEL_IP_COUNTRY |
| AWS CloudFront | HTTP_CLOUDFRONT_VIEWER_COUNTRY |
| Fastly / custom | HTTP_X_COUNTRY_CODE |
| Netlify | HTTP_X_COUNTRY |
| nginx geoip module | HTTP_X_REAL_IP_COUNTRY |
The value is uppercased and validated as a two-letter ISO code; XX and any non-alpha-2 value are rejected and Burst falls through to the local database.
Only trust these headers when your site is actually behind the corresponding edge. A visitor can spoof a request header on a site that is not fronted by a trusted CDN. Use the burst_cdn_country_headers filter to narrow the list to the header your edge sets, or return an empty array to disable built-in header detection entirely.
Database variants
Which database is used is derived from whether Burst Pro is active — there is no setting to configure it.
| Variant | File | Used by | Refresh interval | Data available |
|---|---|---|---|---|
| Country | GeoLite2-Country.tar.gz | Free | Monthly | Country code, continent code |
| City | GeoLite2-City.tar.gz | Pro | Daily | Country, continent, state/region, city, accuracy radius |
When a site upgrades from free to Pro, the on-disk Country database no longer matches the required variant. Pro detects this mismatch on the next admin page load and re-downloads the City database automatically. The country lookup rows already seeded in the burst_locations table are preserved.
Database location
The .mmdb file lives in a directory named after a random 32-character token:
wp-content/uploads/burst/<random-token>/
The token is stored in the burst_maxmind_dir option and the directory is hardened with an index.php and an .htaccess file, so the database cannot be downloaded from a guessable URL. The absolute path to the active .mmdb file is stored in the burst_geo_ip_file option.
Burst also keeps watching the legacy predictable directory wp-content/uploads/burst/maxmind/. A .mmdb file found there is moved into the random directory and burst_geo_ip_file is re-pointed at the new path; leftover archives and temporary extraction files are removed and the legacy directory is deleted. This runs on cron and on admin requests, so a file dropped in the old location is still picked up.
In the Burst section of Tools > Site Health, the directory token is masked as **** in both the database file path and the import error, so a pasted Site Health export does not reveal the download path.
Location data
A successful lookup returns the following fields:
| Field | Type | Description |
|---|---|---|
country_code | string | ISO 3166-1 alpha-2 country code (e.g. US) |
continent_code | string | Two-letter continent code (e.g. EU) |
state | string | Most specific subdivision name — city DB only |
state_code | string | ISO subdivision code — city DB only |
city | string | City name — city DB only |
city_code | int | GeoNames ID for the city — city DB only |
accuracy_radius | int | Accuracy radius in kilometres — city DB only |
All fields are present in the returned array regardless of variant; fields unavailable in the country database (or in an edge/CDN country response) are returned as empty strings or 0. Continent codes use the same seven values as the MaxMind reader (AF, AN, AS, EU, NA, OC, SA).
Database update process
Automatic updates
The database is checked daily via the burst_daily action. If the database has never been downloaded, or was last downloaded longer ago than the refresh interval for the active variant (one month for the free Country database, one day for the Pro City database), it is re-downloaded automatically.
The download process:
- Fetches the SHA-256 hash published alongside the archive on Burst's CDN (
<archive>.sha256). - If a current database is already on disk and its stored hashes still match the published hash, the download is skipped — the existing database is already up to date.
- Creates the GeoIP upload directory if it does not exist yet, and only continues when the directory is available. Without a writable target directory the archive could not be stored, so no download is started at all.
- Downloads the archive whose filename carries the published hash (
GeoLite2-Country-<sha256>.tar.gzfor free,GeoLite2-City-<sha256>.tar.gzfor Pro). The fixed-name archive and its checksum file are cached separately by the CDN, so one can be stale while the other is fresh; a hashed archive is never overwritten and therefore always matches the hash that points to it. When no hash is published, or the hashed archive is missing, Burst falls back to the fixed-name archive (GeoLite2-Country.tar.gzorGeoLite2-City.tar.gz). - Verifies the downloaded archive against the expected SHA-256 hash. On a mismatch the download is discarded, the existing database is kept and the failure is recorded for retry backoff.
- Extracts the
.mmdbfile using PHP'sPharDataextension. - Writes the extracted
.mmdbto a temporary filename inwp-content/uploads/burst/<random-token>/(alongside the current file). - Performs a trial rename to confirm the filesystem supports
rename(). - If the trial succeeds, deletes the existing
.mmdband atomically renames the new file into place. If the trial fails, the temporary file is discarded and the existing database is left untouched. - Cleans up all temporary files (
.tar.gz,.tar, extracted folder). - Stores the path to the active
.mmdbfile in theburst_geo_ip_fileoption, and the archive and extracted-file SHA-256 hashes in theburst_geo_ip_db_hashandburst_geo_ip_file_hashoptions.
Database updates use an atomic write-then-rename flow. If verification, extraction, write, or rename fails at any step, the existing .mmdb is preserved so location lookups continue to work with the previous database.
Existence checks use the local file_exists() call rather than an HTTP probe, which is faster and avoids spurious false positives on restricted hosts.
Download verification and retry backoff
Every downloaded archive is verified against a SHA-256 hash published next to it on Burst's CDN before it is allowed to replace the active database. If the hash cannot be retrieved the download still proceeds; if it is present and does not match, the download is rejected and the previously downloaded database keeps serving lookups.
When a download fails — a verification mismatch, a copy or extraction error, or an unwritable directory — the failure is recorded and the next attempt is delayed with an increasing backoff: 5 minutes, then 1 hour, 6 hours, 1 day, 3 days, and 7 days for each further consecutive failure. A successful download, or a database uploaded manually, clears the backoff.
While a working database is still on disk, a failed refresh reports that the previously downloaded database is still being used, so country tracking keeps working until the download recovers.
Manual upload
If automatic download fails you can upload the .mmdb file manually to the random upload directory (wp-content/uploads/burst/<random-token>/). Read the token from the burst_maxmind_dir option, or read the masked path from Site Health and match it against the directories inside wp-content/uploads/burst/. Burst will detect the file on the next page load, clear any existing import error and reset the retry backoff.
A .mmdb uploaded to the legacy wp-content/uploads/burst/maxmind/ directory also works: Burst moves it into the random directory and points burst_geo_ip_file at the new path.
Preventing automatic updates
To disable automatic database updates (for example, when managing the file manually), define the following constant in wp-config.php:
define( 'BURST_DO_NOT_UPDATE_GEO_IP', true );
Requirements
| Requirement | Notes |
|---|---|
PHP PharData extension | Required to extract the .tar.gz archive. If unavailable, an admin notice is shown. |
| Writable uploads directory | Burst must be able to write to the random GeoIP upload directory inside wp-content/uploads/burst/. When the directory cannot be created, no download is attempted and the path is recorded as an import error. |
No open_basedir restriction on the download path | If detected, an admin notice is shown that names the directory to upload the .mmdb file to manually. |
If the PharData PHP extension is not enabled on your server, the database cannot be extracted automatically. Contact your hosting provider to enable it, or upload the .mmdb file manually.
Hooks & filters
burst_geo_ip_enabled
Controls whether Geo-IP tracking and database management are active. Return false to disable all Geo-IP functionality, including the daily database check, the directory migration, the hit enrichment and the initialization routine.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$enabled | bool | Whether Geo-IP is enabled. Default true. |
Example:
Show code
add_filter( 'burst_geo_ip_enabled', function( $enabled ) {
// Disable Geo-IP tracking entirely.
return false;
} );
burst_country_code_for_ip
Filters the visitor country before the local MaxMind lookup runs. Return a two-letter ISO 3166-1 alpha-2 code to short-circuit the database lookup, or '' (the default) to fall through to the built-in CDN header detection and then the MaxMind database. Use this to plug in your own geolocation source.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
$country_code | string | '' | Empty by default. Return a 2-letter ISO alpha-2 code to override. |
$ip | string | — | The IP address being looked up. |
Example:
Show code
add_filter( 'burst_country_code_for_ip', function( $country_code, $ip ) {
// Resolve the country from a custom source.
return my_lookup_country( $ip ); // e.g. 'US', or '' to fall through.
}, 10, 2 );
The returned code is uppercased and validated as an ISO alpha-2 code; XX and any non-alpha-2 value are ignored and Burst falls through to the next source.
burst_cdn_country_headers
Filters the ordered list of trusted CDN/edge country headers (as $_SERVER keys) that Burst checks for a visitor country. Return an empty array to disable built-in CDN header detection entirely — for example on a site that is not behind a trusted edge and could receive a spoofed header.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$headers | string[] | Ordered list of $_SERVER keys to check. |
Example:
Show code
add_filter( 'burst_cdn_country_headers', function( $headers ) {
// Only trust Cloudflare's header on this site.
return [ 'HTTP_CF_IPCOUNTRY' ];
} );
burst_geoip_handler
Filters the reader class used to resolve and enrich a hit's location. The class must expose a static add_location_data() method. The free plugin uses the country reader by default; Pro registers its city reader through this filter.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$handler | string | Fully-qualified reader class name. Default Burst\Frontend\Tracking\Tracking_GeoIp. |
Example:
Show code
add_filter( 'burst_geoip_handler', function( $handler ) {
// Provide a custom reader class that implements add_location_data().
return My_Custom_GeoIp_Reader::class;
} );
burst_maxmind_download_frequency
Filters how often the Pro City database is refreshed, in seconds. Only the City database honours this filter — the free Country database cadence is fixed at one month and is deliberately not filterable. Lower the value to refresh city data more often, or raise it to reduce download traffic.
Pro - CreatorAvailable in the Creator tier
The City database is a Pro feature. Drill down from country to state, region and city level
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
$frequency | int | DAY_IN_SECONDS | Refresh interval for the City database, in seconds. |
Example:
Show code
add_filter( 'burst_maxmind_download_frequency', function( $frequency ) {
// Refresh the City database every two weeks.
return 2 * WEEK_IN_SECONDS;
} );
burst_location_data
Filters the resolved location array at tracking time, before it is stored. Use this to mock or override the visitor's location in any environment, including when no MaxMind file is present or a localhost IP is detected.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$location_data | array | Associative array of location fields as described in Location data. |
Example:
Show code
add_filter( 'burst_location_data', function( $location_data ) {
// Force every hit to resolve to the Netherlands.
$location_data['country_code'] = 'NL';
$location_data['continent_code'] = 'EU';
return $location_data;
} );
burst_zip_file_path
Filters the full path used when saving the downloaded .tar.gz archive before extraction. By default the archive is saved inside the random GeoIP upload directory.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$zip_file_path | string | Absolute path to the zip file destination. |
Example:
Show code
add_filter( 'burst_zip_file_path', function( $zip_file_path ) {
// Override the download destination path.
return '/custom/path/GeoLite2-City.tar.gz';
} );
burst_localhost_location_data
Filters the filler location data returned for loopback IP addresses (127.0.0.1 and ::1). The filler data is returned whether or not a database file is loaded, so this filter is the way to simulate a specific location during local development.
Parameters:
| Parameter | Type | Description |
|---|---|---|
$location_data | array | Associative array of location fields. Default values represent Groningen, NL. |
Example:
Show code
add_filter( 'burst_localhost_location_data', function( $location_data ) {
// Simulate a US visitor during local development.
return [
'city' => 'New York',
'city_code' => 5128581,
'state' => 'New York',
'state_code' => 'NY',
'country_code' => 'US',
'continent_code' => 'NA',
'accuracy_radius' => 5,
];
} );
burst_tasks (filter)
Burst registers a task notice for Geo-IP import errors via this filter. If the database download fails, an admin notice is added to the Burst tasks list with the error message and a link to the troubleshooting guide. The notice is also served to the MainWP dashboard, so an agency managing child sites sees a failing download there too.
The notice is suppressed immediately after plugin activation (while the database may still be downloading) and is automatically cleared once a valid database file is detected.
WordPress options
The following options are used internally by the Geo-IP system:
| Option | Description |
|---|---|
burst_maxmind_dir | Random 32-character token naming the directory inside wp-content/uploads/burst/ that holds the database file. |
burst_geo_ip_file | Absolute path to the active .mmdb database file. |
burst_last_update_geo_ip | Unix timestamp of the last successful database download. |
burst_geo_ip_last_attempt | Unix timestamp of the last download attempt (throttled to avoid repeated downloads). |
burst_geo_ip_import_error | Error message from the last failed download attempt, or absent if healthy. |
burst_import_geo_ip_on_activation | Flag that triggers a fresh database download on the next admin page load after activation or upgrade. |
burst_geo_ip_db_hash | SHA-256 hash of the downloaded archive from the last successful download, used to skip redundant downloads and to build the hashed archive URL. |
burst_geo_ip_file_hash | SHA-256 hash of the extracted .mmdb file currently on disk. |
burst_geo_ip_failed_attempts | Count of consecutive failed download attempts, driving the retry backoff. |
burst_geo_ip_first_failed_attempt | Unix timestamp of the first failed attempt since the last success. |
burst_geo_ip_last_failed_attempt | Unix timestamp of the most recent failed attempt. |
GDPR considerations
IP addresses are personal data under GDPR. Review your privacy policy and consent setup before enabling Geo-IP tracking.
- IP address lookups are performed server-side using a local database or a country already resolved by your edge/CDN. No IP address is transmitted to MaxMind or any third party at tracking time.
- The MaxMind database itself is downloaded from Burst's CDN (
burst.ams3.cdn.digitaloceanspaces.com). No visitor data is involved in this download. - Burst resolves an IP to a location and then discards the raw IP address (subject to your anonymisation settings). Only the resolved location fields (country, city, etc.) are stored.
- If you operate under a strict consent model, use the
burst_geo_ip_enabledfilter to disable location tracking until consent is granted.
Show code
add_filter( 'burst_geo_ip_enabled', function( $enabled ) {
// Only enable Geo-IP when the visitor has given consent.
return (bool) my_consent_check();
} );