Настройка мультирегионального сайта (разный контент по регионам)

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка мультирегионального сайта (разный контент по регионам)
Сложная
~5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Настройка мультирегионального сайта (разный контент по регионам)

Мультирегиональный сайт — это один домен с разным контентом в зависимости от региона пользователя. Московский пользователь видит актуальные цены для Москвы, краснодарский — свои акции и местные контакты, казахстанский — цены в тенге. Технически задача сложнее, чем кажется: нужно согласовать определение региона, хранение контента, маршрутизацию, 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 недели в зависимости от количества регионов и объёма контента.