Skip to main content

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:

  1. 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.
  2. 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:

ProviderHeader
CloudflareHTTP_CF_IPCOUNTRY
VercelHTTP_X_VERCEL_IP_COUNTRY
AWS CloudFrontHTTP_CLOUDFRONT_VIEWER_COUNTRY
Fastly / customHTTP_X_COUNTRY_CODE
NetlifyHTTP_X_COUNTRY
nginx geoip moduleHTTP_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.

caution

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.

VariantFileUsed byRefresh intervalData available
CountryGeoLite2-Country.tar.gzFreeMonthlyCountry code, continent code
CityGeoLite2-City.tar.gzProDailyCountry, 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:

FieldTypeDescription
country_codestringISO 3166-1 alpha-2 country code (e.g. US)
continent_codestringTwo-letter continent code (e.g. EU)
statestringMost specific subdivision name — city DB only
state_codestringISO subdivision code — city DB only
citystringCity name — city DB only
city_codeintGeoNames ID for the city — city DB only
accuracy_radiusintAccuracy 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:

  1. Fetches the SHA-256 hash published alongside the archive on Burst's CDN (<archive>.sha256).
  2. 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.
  3. 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.
  4. Downloads the archive whose filename carries the published hash (GeoLite2-Country-<sha256>.tar.gz for free, GeoLite2-City-<sha256>.tar.gz for 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.gz or GeoLite2-City.tar.gz).
  5. 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.
  6. Extracts the .mmdb file using PHP's PharData extension.
  7. Writes the extracted .mmdb to a temporary filename in wp-content/uploads/burst/<random-token>/ (alongside the current file).
  8. Performs a trial rename to confirm the filesystem supports rename().
  9. If the trial succeeds, deletes the existing .mmdb and atomically renames the new file into place. If the trial fails, the temporary file is discarded and the existing database is left untouched.
  10. Cleans up all temporary files (.tar.gz, .tar, extracted folder).
  11. Stores the path to the active .mmdb file in the burst_geo_ip_file option, and the archive and extracted-file SHA-256 hashes in the burst_geo_ip_db_hash and burst_geo_ip_file_hash options.

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​

RequirementNotes
PHP PharData extensionRequired to extract the .tar.gz archive. If unavailable, an admin notice is shown.
Writable uploads directoryBurst 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 pathIf detected, an admin notice is shown that names the directory to upload the .mmdb file to manually.
caution

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:

ParameterTypeDescription
$enabledboolWhether 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:

ParameterTypeDefaultDescription
$country_codestring''Empty by default. Return a 2-letter ISO alpha-2 code to override.
$ipstring—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:

ParameterTypeDescription
$headersstring[]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:

ParameterTypeDescription
$handlerstringFully-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

Parameters:

ParameterTypeDefaultDescription
$frequencyintDAY_IN_SECONDSRefresh 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:

ParameterTypeDescription
$location_dataarrayAssociative 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:

ParameterTypeDescription
$zip_file_pathstringAbsolute 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:

ParameterTypeDescription
$location_dataarrayAssociative 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:

OptionDescription
burst_maxmind_dirRandom 32-character token naming the directory inside wp-content/uploads/burst/ that holds the database file.
burst_geo_ip_fileAbsolute path to the active .mmdb database file.
burst_last_update_geo_ipUnix timestamp of the last successful database download.
burst_geo_ip_last_attemptUnix timestamp of the last download attempt (throttled to avoid repeated downloads).
burst_geo_ip_import_errorError message from the last failed download attempt, or absent if healthy.
burst_import_geo_ip_on_activationFlag that triggers a fresh database download on the next admin page load after activation or upgrade.
burst_geo_ip_db_hashSHA-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_hashSHA-256 hash of the extracted .mmdb file currently on disk.
burst_geo_ip_failed_attemptsCount of consecutive failed download attempts, driving the retry backoff.
burst_geo_ip_first_failed_attemptUnix timestamp of the first failed attempt since the last success.
burst_geo_ip_last_failed_attemptUnix timestamp of the most recent failed attempt.

GDPR considerations​

caution

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_enabled filter 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();
} );