How to Get User Location by IP Address in WordPress

Geolocation lets you show or hide content based on the visitor’s country — useful for region-restricted products, localised promotions, or GDPR-specific consent flows. The approach: detect the client IP, query a geolocation API, and act on the result.

Problem: How do you detect a visitor's country or city in WordPress — for example, to show localised content or restrict access by geography — without requiring them to fill in a form?

Solution: Retrieve the visitor's IP address from $_SERVER (checking HTTP_X_FORWARDED_FOR for proxy headers first), then query a free geolocation API such as ip-api.com via wp_remote_get() and cache the result in a transient to avoid hitting the rate limit on every request.

A reliable helper that handles proxy headers and falls back gracefully:

function get_client_ip() {
    $headers = [
        'HTTP_CF_CONNECTING_IP',    // Cloudflare
        'HTTP_X_FORWARDED_FOR',     // load balancers / proxies
        'HTTP_CLIENT_IP',
        'REMOTE_ADDR',              // direct connection
    ];

    foreach ( $headers as $header ) {
        $ip = getenv( $header ) ?: ( $_SERVER[ $header ] ?? '' );
        if ( $ip ) {
            // X_FORWARDED_FOR may contain a list — take the first one
            $ip = trim( explode( ',', $ip )[0] );
            if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
                return $ip;
            }
        }
    }

    return '';
}

function get_client_country() {
    $ip      = get_client_ip();
    $api_key = defined( 'IPGEO_API_KEY' ) ? IPGEO_API_KEY : '';

    if ( ! $ip || ! $api_key ) return '';

    $transient = 'geo_country_' . md5( $ip );
    $cached    = get_transient( $transient );
    if ( $cached !== false ) return $cached;

    $url      = "https://api.ipgeolocation.io/ipgeo?apiKey={$api_key}&ip={$ip}&fields=country_name";
    $response = wp_remote_get( esc_url_raw( $url ), [ 'timeout' => 3 ] );

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

    $data    = json_decode( wp_remote_retrieve_body( $response ), true );
    $country = $data['country_name'] ?? '';

    set_transient( $transient, $country, HOUR_IN_SECONDS );
    return $country;
}

// Redirect non-US visitors away from US-only product pages
add_action( 'template_redirect', function() {
    if ( is_singular( 'product' ) && get_field( 'show_for_usa' ) ) {
        if ( get_client_country() !== 'United States' ) {
            wp_redirect( home_url() );
            exit;
        }
    }
} );

Add your API key to wp-config.php (register for free at ipgeolocation.io):

define( 'IPGEO_API_KEY', 'your_api_key_here' );

NOTE: Cache the geolocation result per IP with a transient (as shown above) to avoid hitting the API on every page load. Free-tier plans typically allow 1,000 requests per day — without caching, a single user browsing multiple pages would exhaust that quota in minutes. Also note that IP-based geolocation is not 100% accurate and should not be used as the sole mechanism for legally required access control.