Geo-Based IP Redirect с Cloudflare и WordPress: Пренасочване към друг домейн по държава без плъгини

Имате WooCommerce магазин, обслужва клиенти от България и втори домейн, който продава в Румъния. Българските потребители виждат българския домейн и искате да насочите румънксите потребители към румънския сайт.

Проблемът: WordPress трябва да разбере откъде идва всеки посетител, преди да зареди каквото и да било. Повечето geo-redirect плъгини правят точно това – изпълняват PHP логика при всяка заявка, убиват page cache-а и добавят 200-400ms латентност.

Как румънският потребител автоматично попада на румънския домейн, където всичко е на езика и валутата, които очаква? Ще разьгледаме два подхода, които работят добре за тази задача – единият изисква Cloudflare Workers, другият е обикновен PHP snippet директно в WordPress.

Защо плъгините за geo-redirect забавят WooCommerce

Повечето WordPress плъгини за geo-redirect (IP2Location, GeoTargetingWP, Redirection с geo rules) работят на PHP ниво. При всяка заявка WordPress зарежда целия стек – wp-load.php, wp-settings.php, активни плъгини, theme functions – само за да провери IP адреса и да реши дали да пренасочи.

Това поведение прави page caching почти невъзможен. Ако Varnish или LiteSpeed Cache върне кеширана българска страница на румънски потребител, резултатът е грешен. Затова тези плъгини обикновено добавят DONOTCACHEPAGE константа или изключват определени URL-и от кеша. TTFB скача от 150ms на 600-800ms при всяко първо зареждане, защото всяка заявка минава през пълния PHP стек. За магазин, който разчита на добри Core Web Vitals резултати, това е неприемливо.

Двата подхода по-долу избягват тези плъгини изцяло.

Вариант 1: PHP snippet с CF-IPCountry хедър

Това е най-простият работещ вариант. Cloudflare автоматично добавя хедъра CF-IPCountry към всяка заявка, която минава през мрежата. Единственото условие – домейнът да е зад Cloudflare proxy (оранжевото облаче в DNS настройките). Базовата Cloudflare конфигурация отнема 10 минути.

PHP кодът се поставя в functions.php на child темата или в Code Snippets плъгин на българския домейн:

add_action('template_redirect', function() {
    if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
        return;
    }

    if (defined('REST_REQUEST') && REST_REQUEST) {
        return;
    }

    $country = strtoupper(
        sanitize_text_field($_SERVER['HTTP_CF_IPCOUNTRY'] ?? '')
    );

    if ($country === 'RO') {
        wp_redirect('https://example.ro', 302);
        exit;
    }
}, 1);

Приоритетът 1 в add_action е съществен. WordPress изпълнява template_redirect хуковете по приоритет – стойност 1 означава, че пренасочването става преди темата, преди WooCommerce (приоритет 10+), преди аналитичните плъгини. По-малко PHP код се изпълнява, по-бързо идва redirect-ът.

Какво проверява кодът преди пренасочване

is_admin() спира изпълнението за /wp-admin/ заявки. wp_doing_ajax() предпазва AJAX повиквания от WooCommerce количката и checkout формите. wp_doing_cron() изключва WP-Cron задачите. REST_REQUEST константата предпазва REST API endpoint-и, които може да използват други плъгини или мобилни приложения.

Без тези проверки redirect-ът може да счупи admin панела при достъп от румънски IP, да прекъсне AJAX добавянето в количката или да блокира API интеграции.

Ограничения на PHP подхода

PHP snippet-ът изпълнява WordPress частично преди redirect-а. template_redirect се задейства след wp-load.php и wp-settings.php, което означава 50-150ms PHP overhead дори при най-бързия хостинг. За повечето сайтове това е напълно приемливо – потребителят вижда redirect за под 200ms.

По-сериозният проблем е кеширането. Ако сървърът използва full-page cache (Varnish, LiteSpeed, nginx FastCGI), кешираната страница се сервира преди PHP да се задейства. Румънски потребител получава кеширания български HTML вместо redirect. Решението е да се добави Cache-Vary хедър по CF-IPCountry или да се изключат redirect страниците от кеша.

add_action('send_headers', function() {
    if (!is_admin()) {
        header('Vary: CF-IPCountry', false);
    }
});

Този Vary хедър казва на кеш слоя, че отговорът зависи от държавата на потребителя. LiteSpeed и Varnish ще пазят отделни кеш копия за BG и RO трафик. Минусът: кешът става по-голям и по-малко ефективен, защото всяка страница се съхранява в няколко варианта.

Вариант 2: Cloudflare Workers – redirect на edge ниво

Cloudflare Workers изпълнява JavaScript на edge сървъра, най-близо до потребителя, преди заявката да достигне WordPress. Няма PHP overhead. Няма конфликт с page cache. Redirect-ът се случва за под 5ms.

Безплатният план на Workers включва 100 000 заявки на ден – достатъчно за повечето малки и средни магазини.

Създаване на Worker

В Cloudflare Dashboard, секция Workers & Pages, натиснете Create Application и после Create Worker. Дайте описателно име – например geo-redirect-ro. Cloudflare генерира preview URL, но за production Worker-ът ще се свърже с route на българския домейн.

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const country = request.headers.get('CF-IPCountry') || 'XX';
    const path = url.pathname;

    // Skip assets, admin, API, AJAX
    if (
      path.startsWith('/wp-admin') ||
      path.startsWith('/wp-json') ||
      path.startsWith('/wp-content') ||
      path.match(/.(js|css|png|jpg|webp|svg|woff2?)$/) ||
      url.searchParams.has('wc-ajax')
    ) {
      return fetch(request);
    }

    // Romanian visitors -> Romanian domain
    if (country === 'RO') {
      return Response.redirect('https://example.ro' + path, 302);
    }

    return fetch(request);
  }
};

Worker-ът пренасочва с 302 (temporary redirect). 301 би бил кеширан от браузъра и потребител, който пътува между държавите, ще вижда грешния домейн без начин да се коригира.

Route конфигурация

След deploy, в секцията Triggers добавете route: example.bg/*. Worker-ът ще се задейства за всички заявки към българския домейн. wc-ajax проверката в кода предпазва WooCommerce AJAX количката от пренасочване по средата на checkout.

За WooCommerce webhook-ове и API интеграции с ERP или счетоводен софтуер, добавете допълнителни изключения. Webhook URL-ите обикновено минават през /wp-json/wc/v3/, което вече е покрито, но custom endpoint-и може да изискват отделна проверка.

Автоматичното пренасочване създава проблем: румънски потребител, който иска да поръча от българския магазин (по-ниски цени, различен продуктов каталог), не може да остане на .bg домейна. IP-базираната геолокация не е 100% точна – VPN потребители, корпоративни мрежи и мобилни оператори с routing през друга държава ще получат грешен redirect.

Cookie override механизмът е задължителен.

За PHP варианта, добавете проверка в началото на snippet-а:

add_action('template_redirect', function() {
    if (isset($_COOKIE['skip_geo_redirect'])) {
        return;
    }

    if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
        return;
    }

    if (defined('REST_REQUEST') && REST_REQUEST) {
        return;
    }

    $country = strtoupper(
        sanitize_text_field($_SERVER['HTTP_CF_IPCOUNTRY'] ?? '')
    );

    if ($country === 'RO') {
        wp_redirect('https://example.ro', 302);
        exit;
    }
}, 1);

На фронтенда, language switcher или банер „Искате да останете на българския сайт?“ сетва skip_geo_redirect cookie с JavaScript:

document.cookie = 'skip_geo_redirect=1; max-age=2592000; path=/; SameSite=Lax';

30 дни (2592000 секунди) е разумен баланс – достатъчно дълго, за да не досажда, достатъчно кратко, за да не е постоянно.

За Workers варианта, добавете cookie проверка преди redirect логиката:

const cookies = request.headers.get('Cookie') || '';
if (cookies.includes('skip_geo_redirect=1')) {
  return fetch(request);
}

SEO: hreflang и индексиране при два домейна

Google трябва да знае, че .bg и .ro домейните са езикови варианти на един бизнес, а не дублирано съдържание. В head секцията на всяка страница и на двата домейна:

Googlebot обикновено идентифицира себе си с CF-IPCountry: US. Ако нито PHP snippet-ът, нито Worker-ът пренасочва US трафик (а и не трябва), Google ще индексира българския домейн нормално. Румънският домейн ще се индексира самостоятелно от Google Romania.

302 redirect е правилният код за geo-базирано пренасочване. 301 казва на Google „тази страница се е преместила завинаги“, което не е вярно – страницата съществува и на двата домейна.

PHP snippet или Workers: кога кой вариант

PHP вариантът е подходящ, когато сайтът вече е зад Cloudflare, хостингът няма агресивен full-page cache и трафикът е умерен – до 50 000 посещения месечно. Поддръжката е проста: един snippet в functions.php или Code Snippets плъгин, който всеки WordPress разработчик разбира.

Workers вариантът е по-добър за магазини с висок трафик, агресивно кеширане (Varnish, LiteSpeed Enterprise, Cloudflare APO) и нужда от нулев PHP overhead. Redirect-ът се случва преди origin сървъра да разбере, че има заявка. За сайтове, където всяка милисекунда има значение, edge redirect-ът е правилният избор.

И двата варианта работят с WebP оптимизация на изображенията и останалите front-end оптимизации без конфликт. Geo-redirect логиката е напълно независима от rendering pipeline-а.

Тестване без пътуване до Румъния

Curl с custom хедър тества PHP варианта директно:

curl -I -H "CF-IPCountry: RO" https://example.bg/

Очакваният отговор е HTTP 302 с Location: https://example.ro. Ако вместо това получите 200 и HTML съдържание, page cache-ът сервира преди PHP. Изчистете кеша и тествайте отново, или добавете Vary хедъра от секцията за ограничения.

VPN от Румъния дава пълен end-to-end тест – DNS resolution, Cloudflare edge routing, WordPress redirect. Windscribe и ProtonVPN имат безплатни румънски сървъри.

За Workers варианта, Cloudflare Dashboard показва real-time логове в секция Workers > Logs. Всеки redirect се вижда с country code, URL и HTTP status.

Често задавани въпроси

  1. Работи ли geo-redirect без Cloudflare?

    PHP snippet-ът разчита на CF-IPCountry хедъра, който добавя само Cloudflare. Без Cloudflare е необходима алтернативна geo-IP база (MaxMind GeoLite2), което добавя допълнителна сложност и заявка към база данни при всяко зареждане.

  2. 302 или 301 redirect за пренасочване по държава?

    Винаги 302 (temporary). 301 се кешира от браузъра и казва на Google, че страницата се е преместила завинаги. При geo-redirect и двата URL-а са валидни – просто обслужват различни аудитории.

  3. Колко струва Cloudflare Workers за geo-redirect?

    Безплатният план включва 100 000 заявки дневно. За повечето малки и средни магазини това е достатъчно. Платеният план е $5/месец за 10 милиона заявки.

  4. Какво става с румънски потребител, който иска да купи от българския магазин?

    Cookie override механизмът позволява ръчна смяна. Банер или бутон сетва skip_geo_redirect cookie и redirect-ът спира за 30 дни.

  5. Ще счупи ли geo-redirect WooCommerce количката?

    Не, ако AJAX заявките са изключени от redirect логиката. PHP snippet-ът проверява wp_doing_ajax(), а Workers вариантът филтрира wc-ajax параметъра.

Сподели: