GeoIP Redirect

Redirect first-time visitors to their country's language based on IP geolocation.

How It Works

  1. A first-time visitor (no language cookie) arrives at the site
  2. PerfLocale detects their IP address (supports proxies, Cloudflare, load balancers)
  3. The configured GeoIP provider returns the visitor's country code
  4. The country code is mapped to a language via the Country Mapping table
  5. If the mapped language is active and different from the default, a 302 redirect occurs
  6. A cookie is set to prevent future redirects

GeoIP redirect takes priority over Browser Language Redirect. If both are enabled and GeoIP finds a match, the browser redirect is skipped.

Built-in Providers

All providers use HTTPS exclusively. Providers that only offer HTTPS on paid plans require a paid subscription.

ProviderAPI KeyFree TierNotes
ipinfo.ioOptional1,000/day (no token), 50,000/month (with token)HTTPS, default provider. Also works for paid plans.
ipinfo.io LiteRequiredUnlimitedFree signup, country-level data only
ipapi.coNot needed1,000 requests/dayHTTPS, non-commercial use only on free tier
ipstack.comRequiredN/AHTTPS requires paid plan
ip-api.comRequiredN/AHTTPS requires Pro plan

Settings

Enable GeoIP Redirect

Checkbox to enable/disable the feature. When disabled, no API calls are made.

GeoIP Provider

Select which API to use for IP lookups. Provider-specific fields (API keys) are shown/hidden based on selection.

API Keys

API keys can be stored in wp-config.php as constants:

define( 'PERFLOCALE_IPINFO_TOKEN', 'your-token-here' );
define( 'PERFLOCALE_IPINFO_LITE_TOKEN', 'your-lite-token-here' );
define( 'PERFLOCALE_IPSTACK_KEY', 'your-key-here' );
define( 'PERFLOCALE_IP_API_KEY', 'your-ip-api-pro-key-here' );

When defined as constants, the settings fields are disabled and show the constant name.

Custom IP Header

If your site is behind a CDN or reverse proxy that uses a custom header for the real visitor IP (e.g., Imperva, Sucuri, or a custom load balancer), use the perflocale/geo/visitor_ip filter to read it:

// Example: Imperva (Incapsula) uses X-Incap-Client-IP.
add_filter( 'perflocale/geo/visitor_ip', function ( string $ip ): string {
	if ( ! empty( $_SERVER['HTTP_X_INCAP_CLIENT_IP'] ) ) {
		return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_INCAP_CLIENT_IP'] ) );
	}
	return $ip;
} );

PerfLocale automatically detects Cloudflare (CF-Connecting-IP), standard proxies (X-Forwarded-For), and nginx (X-Real-IP). Use this filter only if your proxy sends a non-standard header.

Cache Duration

GeoIP results are cached per IP address using WordPress transients. Default: 24 hours. This prevents repeated API calls for the same visitor and respects provider rate limits.

Country Mapping

A table of active languages with comma-separated country codes. Auto-populated from each language's flag field. Examples:

LanguageCountry Codes
EnglishUS, GB, AU, NZ, CA
GermanDE, AT, CH
SpanishES, MX, AR, CO, CL
ArabicSA, AE, EG, MA

Multiple countries can map to the same language. Each country code can only map to one language - the first match wins.

Developer Hooks

Filters

perflocale/geo/lookup_country

Bypass all built-in providers by returning a country code from your own source (local database, custom API, etc.).

// Use MaxMind GeoLite2 local database instead of API calls.
add_filter( 'perflocale/geo/lookup_country', function ( string $country, string $ip ): string {
	if ( ! class_exists( 'GeoIp2\Database\Reader' ) ) {
		return $country;
	}

	try {
		$reader  = new GeoIp2\Database\Reader( '/path/to/GeoLite2-Country.mmdb' );
		$record  = $reader->country( $ip );
		return $record->country->isoCode;
	} catch ( \Exception $e ) {
		return $country; // Fall back to built-in provider.
	}
}, 10, 2 );

Parameters:

  • string $country_code - Empty string (return a 2-letter code to skip built-in providers).
  • string $ip - Visitor IP address.

perflocale/geo/country_code

Modify the country code after it has been looked up by any provider.

add_filter( 'perflocale/geo/country_code', function ( string $code, string $ip ): string {
	// Custom logic here.
	return $code;
}, 10, 2 );

Parameters:

  • string $country_code - Two-letter country code (uppercase).
  • string $ip - Visitor IP address.

perflocale/geo/redirect_language

Override which language a visitor should be redirected to, after country-to-language mapping.

// Force all South American countries to Spanish.
add_filter( 'perflocale/geo/redirect_language', function ( string $slug, string $country, string $ip ): string {
	$south_america = [ 'AR', 'BO', 'BR', 'CL', 'CO', 'EC', 'GY', 'PY', 'PE', 'SR', 'UY', 'VE' ];

	if ( in_array( $country, $south_america, true ) && $country !== 'BR' ) {
		return 'es';
	}

	return $slug;
}, 10, 3 );

Parameters:

  • string $language_slug - Resolved language slug (or empty if no mapping found).
  • string $country_code - Two-letter country code.
  • string $ip - Visitor IP address.

perflocale/geo/providers

Register custom GeoIP providers alongside the built-in ones.

add_filter( 'perflocale/geo/providers', function ( array $providers ): array {
	$providers['my_provider'] = [
		'name'           => 'My GeoIP Service',
		'needs_key'      => true,
		'key_setting'    => 'geo_my_provider_key',
		'fetch_callback' => function ( string $ip, $settings ): string {
			$key = $settings->get( 'geo_my_provider_key', '' );
			$response = wp_remote_get( "https://my-api.com/lookup?ip={$ip}&key={$key}" );

			if ( is_wp_error( $response ) ) {
				return '';
			}

			$body = json_decode( wp_remote_retrieve_body( $response ), true );
			return $body['country'] ?? '';
		},
	];

	return $providers;
} );

Parameters:

  • array $providers - Associative array of provider definitions.

perflocale/geo/country_map

Filter the country-to-language mapping array.

// Programmatically add mappings.
add_filter( 'perflocale/geo/country_map', function ( array $map ): array {
	$map['JP'] = 'ja';
	$map['KR'] = 'ko';
	return $map;
} );

Parameters:

  • array $map - [ 'US' => 'en', 'DE' => 'de', ... ]

Actions

perflocale/geo/redirected

Fires after a GeoIP redirect is performed. Useful for analytics or logging.

add_action( 'perflocale/geo/redirected', function ( string $slug, string $country, string $ip ): void {
	error_log( "PerfLocale GeoIP: Redirected {$ip} ({$country}) to {$slug}" );
}, 10, 3 );

Parameters:

  • string $language_slug - Language the visitor was redirected to.
  • string $country_code - Detected country code.
  • string $ip - Visitor IP address.

Behavior Notes

  • Local/private IPs (127.0.0.1, 192.168.x.x, etc.) are skipped - no API call is made.
  • Bots and crawlers are excluded from redirects (same as browser redirect).
  • Caching prevents API abuse - each IP is looked up once per cache duration.
  • Empty results are cached too - prevents hammering the API for unresolvable IPs.
  • Cookie prevents re-redirect - once a visitor has a language cookie, GeoIP is skipped.
  • Priority: URL detection > Cookie > GeoIP redirect > Browser redirect > Default language.