Налаштування мультирегіонального сайту (різний контент по регіонах)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування мультирегіонального сайту (різний контент по регіонах)
Складна
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Налаштування мультирегіонального сайту (різний контент по регіонах)

Мультирегіональний сайт — це один домен з різним контентом в залежності від регіону користувача. Московський користувач бачит актуальні ціни для Москви, краснодарський — свої акції та місцеві контакти, казахстанський — ціни в тенге. Технічно завдання складніше, ніж здається: потрібно узгодити визначення регіону, зберігання контенту, маршрутизацію, SEO та кешування.

Архітектурні варіанти

Варіант 1: Поддиректоріїsite.ru/msk/, site.ru/spb/, site.ru/krd/ Найпростіше для SEO, зрозуміло для користувача, легко реалізується на будь-якому фреймворку. Google індексує кожен регіон окремо.

Варіант 2: Поддомениmsk.site.ru, spb.site.ru Вимагає wildcard-сертифіката та DNS-запису *.site.ru. Простіше розділити кеш по регіонах на рівні CDN. Технічно чистіше, але Google може сприйняти поддомени як різні сайти — гірше для загального авторитету домену.

Варіант 3: Автоопределення без зміни URL Користувач завжди на site.ru, регіон визначається по IP. Погано для SEO — Googlebot не обходить контент різних регіонів, завжди бачит один. Підходить лише якщо регіональний контент не потрібно індексувати.

Для більшості проектів оптимален варіант 1.

Модель даних

CREATE TABLE regions (
    id          SERIAL PRIMARY KEY,
    slug        VARCHAR(16) UNIQUE NOT NULL, -- 'msk', 'spb', 'krd'
    name        VARCHAR(128) NOT NULL,
    is_default  BOOLEAN DEFAULT false,
    currency    VARCHAR(3) DEFAULT 'RUB',
    phone       VARCHAR(32),
    address     TEXT
);

-- Регіональні переопределення контенту
CREATE TABLE content_region_overrides (
    content_id  INTEGER NOT NULL,
    region_id   INTEGER NOT NULL REFERENCES regions(id),
    field       VARCHAR(64) NOT NULL, -- 'price', 'title', 'body'
    value       TEXT,
    PRIMARY KEY (content_id, region_id, field)
);

Базовий контент зберігається в основній таблиці. Регіональні переопределення — лише те, що відрізняється. Це економить місце та спрощує синхронізацію: при зміні базового контенту регіони, у яких немає override, автоматично отримують нову версію.

Маршрутизація (Laravel)

// routes/web.php
Route::prefix('{region}')
    ->where(['region' => '[a-z]{2,8}'])
    ->middleware('region.resolve')
    ->group(function () {
        Route::get('/', [HomeController::class, 'index']);
        Route::get('/catalog/{slug}', [CatalogController::class, 'show']);
        Route::get('/contacts', [ContactsController::class, 'index']);
    });

// Корень без регіону — редирект на визначений регіон
Route::get('/', RegionDetectController::class);
// app/Http/Middleware/ResolveRegion.php
public function handle(Request $request, Closure $next): Response
{
    $slug = $request->route('region');
    $region = Region::where('slug', $slug)->firstOrFail();

    // Доступна у всьому додатку через singleton
    app()->instance('current.region', $region);
    View::share('currentRegion', $region);

    return $next($request);
}

Визначення регіону при першому заході

// app/Http/Controllers/RegionDetectController.php
public function __invoke(Request $request): RedirectResponse
{
    // 1. Збережений регіон у куці
    if ($saved = $request->cookie('preferred_region')) {
        if (Region::where('slug', $saved)->exists()) {
            return redirect("/{$saved}/");
        }
    }

    // 2. Визначення по IP через MaxMind GeoIP2
    $reader = new \GeoIp2\Database\Reader(storage_path('geoip/GeoLite2-City.mmdb'));
    try {
        $record = $reader->city($request->ip());
        $citySlug = $this->mapCityToRegion($record->city->name);
    } catch (\Exception) {
        $citySlug = null;
    }

    $slug = $citySlug ?? Region::where('is_default', true)->value('slug');

    return redirect("/{$slug}/")->withCookie(
        cookie('preferred_region', $slug, 60 * 24 * 365)
    );
}

Отримання регіонального контенту

trait HasRegionalContent
{
    public function getRegionalField(string $field, ?Region $region = null): mixed
    {
        $region ??= app('current.region');

        $override = ContentRegionOverride::where('content_id', $this->id)
            ->where('region_id', $region->id)
            ->where('field', $field)
            ->value('value');

        return $override ?? $this->$field;
    }
}

Використання: $product->getRegionalField('price') — вернє регіональну ціну або базову, якщо override не задан.

SEO: hreflang та sitemap

Для коректної регіональної індексації кожна сторінка повинна містити hreflang-теги:

<link rel="alternate" hreflang="ru-RU" href="https://site.ru/msk/catalog/product-1" />
<link rel="alternate" hreflang="ru-KZ" href="https://site.ru/kz/catalog/product-1" />
<link rel="alternate" hreflang="x-default" href="https://site.ru/msk/catalog/product-1" />

Регіональний sitemap генерується окремо для кожного регіону та включається в sitemap_index.xml.

Кешування

При використанні Nginx або Varnish ключ кешу повинен включати регіон:

# nginx fastcgi_cache
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# URI вже містить /msk/ — кеш автоматично розділяється по регіонах

Для Redis-кешу Laravel:

$cacheKey = "catalog.{$region->slug}.{$slug}";
Cache::remember($cacheKey, 3600, fn() => $this->buildPage($slug, $region));

Адміністративна частина

У CMS потрібно передбачити:

  • Переключатель регіону в інтерфейсі редагування
  • Візуальне виділення полів з регіональним override
  • Масове застосування override на групу товарів
  • Звіт: які сторінки мають регіональні версії, а які ні

Строки та етапи

Етап Вміст Строк
1 Модель даних, міграції, CRUD регіонів 2 дні
2 Маршрутизація, middleware, GeoIP 2 дні
3 Регіональний контент у шаблонах 3 дні
4 SEO: hreflang, sitemap 1 день
5 Адміністративний інтерфейс 3 дні
6 Кешування, нагрузкове тестування 2 дні

Всього: 2–3 тижні в залежності від кількості регіонів та обсягу контенту.