Настройка мультидоменного сайта: полное руководство 2026

За 10 лет работы с веб-проектами я настроил десятки мультидоменных сайтов — от простых корпоративных порталов до сложных интернет-магазинов с региональными версиями. И честно говоря, многие разработчики до сих пор делают критические ошибки в этой области.

Что такое мультидоменный сайт и зачем он нужен

Мультидоменный сайт — это веб-ресурс, который работает на нескольких доменах одновременно. Например, основной сайт на example.com, а его региональные версии на spb.example.com, moscow.example.com или отдельные домены типа example.ru, example.ua.

На моей практике чаще всего встречаются такие сценарии:

У одного моего клиента была сеть автосалонов в 15 городах. Каждый город требовал отдельного домена с локальным контентом, но при этом единой системы управления. Именно тогда я понял, насколько важно правильно спроектировать архитектуру с самого начала.

Основные преимущества мультидоменного подхода:

Но есть и минусы — сложность настройки, дублирование контента, проблемы с SSL-сертификатами и увеличенные расходы на хостинг.

Виды мультидоменных настроек

За годы практики я выделил несколько основных архитектурных подходов к мультидоменным сайтам. Каждый имеет свои особенности и область применения.

Поддомены (субдомены)

Самый простой вариант — использование поддоменов типа city.example.com. Я часто рекомендую именно этот подход для начинающих проектов. Настройка происходит на уровне DNS и веб-сервера, без необходимости регистрировать дополнительные домены.

Плюсы:

Минусы:

Отдельные домены

Полностью независимые домены — example.com, example.ru, example-spb.com. Этот подход я использую для крупных проектов с серьёзными амбициями по региональному продвижению.

У меня был проект интернет-магазина, где владелец хотел выйти на рынки России, Украины и Казахстана. Мы сделали отдельные домены с национальными зонами — это дало существенный boost в локальной выдаче.

Папки на основном домене

Вариант с example.com/region/ технически не является мультидоменным, но решает похожие задачи. Я его упоминаю, потому что многие путают эти подходы.

⚠️
Важно: Не смешивайте разные подходы в одном проекте без веской причины. Это создаёт проблемы с индексацией и путает пользователей.

Настройка веб-сервера для мультидомена

Основа любого мультидоменного сайта — правильная настройка веб-сервера. Я работаю в основном с Apache и Nginx, и у каждого есть свои нюансы.

Настройка Apache

В Apache для мультидоменности используются виртуальные хосты (Virtual Hosts). Вот базовая конфигурация, которую я использую:

# Основной домен
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com
    
    # Переменная окружения для определения домена
    SetEnv SITE_DOMAIN "main"
    
    # Логи для каждого домена отдельно
    ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
    CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
</VirtualHost>

# Региональный домен
<VirtualHost *:80>
    ServerName spb.example.com
    DocumentRoot /var/www/example.com
    
    SetEnv SITE_DOMAIN "spb"
    
    ErrorLog ${APACHE_LOG_DIR}/spb.example.com_error.log
    CustomLog ${APACHE_LOG_DIR}/spb.example.com_access.log combined
</VirtualHost>

# Отдельный домен
<VirtualHost *:80>
    ServerName example.ru
    ServerAlias www.example.ru
    DocumentRoot /var/www/example.com
    
    SetEnv SITE_DOMAIN "ru"
    
    ErrorLog ${APACHE_LOG_DIR}/example.ru_error.log
    CustomLog ${APACHE_LOG_DIR}/example.ru_access.log combined
</VirtualHost>

Ключевой момент здесь — использование переменной окружения SITE_DOMAIN. Это позволяет PHP-коду определить, на каком домене работает сайт, и подключить соответствующие настройки.

Настройка Nginx

С Nginx я предпочитаю работать из-за его производительности. Вот моя типовая конфигурация:

# Основной домен
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example.com;
    index index.php index.html;
    
    # Передаём домен в PHP
    fastcgi_param SITE_DOMAIN "main";
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SITE_DOMAIN "main";
        include fastcgi_params;
    }
    
    access_log /var/log/nginx/example.com.access.log;
    error_log /var/log/nginx/example.com.error.log;
}

# Поддомен
server {
    listen 80;
    server_name spb.example.com;
    root /var/www/example.com;
    index index.php index.html;
    
    fastcgi_param SITE_DOMAIN "spb";
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SITE_DOMAIN "spb";
        include fastcgi_params;
    }
    
    access_log /var/log/nginx/spb.example.com.access.log;
    error_log /var/log/nginx/spb.example.com.error.log;
}

Обратите внимание — я дублирую fastcgi_param SITE_DOMAIN в двух местах. Это нужно для корректной работы как со статическими файлами, так и с PHP-скриптами.

💡
Совет: Всегда ведите отдельные логи для каждого домена. Это сильно упрощает отладку проблем и анализ трафика по доменам.

Программная логика мультидомена

После настройки веб-сервера нужно написать код, который будет определять текущий домен и подключать соответствующие настройки. Я покажу примеры для разных CMS и фреймворков.

Чистый PHP

Базовая логика определения домена в PHP выглядит так:

<?php
class MultisiteManager 
{
    private $domains = [
        'example.com' => [
            'site_id' => 'main',
            'language' => 'ru',
            'currency' => 'RUB',
            'theme' => 'default'
        ],
        'spb.example.com' => [
            'site_id' => 'spb',
            'language' => 'ru',
            'currency' => 'RUB',
            'theme' => 'regional'
        ],
        'example.com.ua' => [
            'site_id' => 'ua',
            'language' => 'uk',
            'currency' => 'UAH',
            'theme' => 'ukraine'
        ]
    ];
    
    private $currentSite;
    
    public function __construct() 
    {
        $this->detectCurrentSite();
    }
    
    private function detectCurrentSite() 
    {
        $host = $_SERVER['HTTP_HOST'] ?? '';
        
        // Убираем www если есть
        $host = preg_replace('/^www\./', '', $host);
        
        // Проверяем переменную окружения от веб-сервера
        $envDomain = $_ENV['SITE_DOMAIN'] ?? $_SERVER['SITE_DOMAIN'] ?? null;
        
        if ($envDomain && isset($this->domains[$host])) {
            $this->currentSite = $this->domains[$host];
            $this->currentSite['domain'] = $host;
            return;
        }
        
        // Fallback на основной домен
        $mainDomain = 'example.com';
        $this->currentSite = $this->domains[$mainDomain];
        $this->currentSite['domain'] = $mainDomain;
    }
    
    public function getCurrentSite() 
    {
        return $this->currentSite;
    }
    
    public function getSiteId() 
    {
        return $this->currentSite['site_id'];
    }
    
    public function getLanguage() 
    {
        return $this->currentSite['language'];
    }
    
    public function getTheme() 
    {
        return $this->currentSite['theme'];
    }
}

// Использование
$multisite = new MultisiteManager();
$siteConfig = $multisite->getCurrentSite();

echo "Текущий сайт: " . $multisite->getSiteId() . "\n";
echo "Язык: " . $multisite->getLanguage() . "\n";
?>

Эту логику я использую как базу практически во всех проектах. Она позволяет централизованно управлять настройками для каждого домена.

WordPress Multisite

В WordPress есть встроенная поддержка мультисайтов. Активируется она добавлением строк в wp-config.php:

// Включаем мультисайт
define('WP_ALLOW_MULTISITE', true);

// После настройки через админку добавляются:
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', true); // или false для папок
define('DOMAIN_CURRENT_SITE', 'example.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);

Но честно говоря, стандартный WordPress Multisite меня не всегда устраивает. Слишком много ограничений и костылей. Для серьёзных проектов я предпочитаю кастомные решения.

Битрикс мультисайт

В Битрикс мультисайт настраивается через админку, но требует доработки на уровне кода. Я обычно создаю компонент для определения сайта:

<?php
// /local/php_interface/init.php

use Bitrix\Main\Context;
use Bitrix\Main\SiteTable;

class BitrixMultisite 
{
    public static function getCurrentSiteId() 
    {
        $request = Context::getCurrent()->getRequest();
        $host = $request->getHttpHost();
        
        // Ищем сайт по домену
        $site = SiteTable::getList([
            'filter' => [
                '=DOMAINS.DOMAIN' => $host
            ],
            'select' => ['LID', 'NAME']
        ])->fetch();
        
        return $site ? $site['LID'] : 's1';
    }
    
    public static function setSiteContext() 
    {
        $siteId = self::getCurrentSiteId();
        define('SITE_ID', $siteId);
        
        // Подключаем настройки сайта
        $context = Context::getCurrent();
        $context->setSite($siteId);
    }
}

// Вызываем при инициализации
BitrixMultisite::setSiteContext();
?>

У меня был случай с крупным холдингом — 8 компаний, каждая со своим сайтом, но единой админкой. Битрикс справился, но пришлось серьёзно дорабатывать стандартную логику.

База данных и контент

Одна из главных задач мультидоменного сайта — правильная организация данных. Я встречал проекты, где из-за неправильной архитектуры БД приходилось полностью переписывать логику.

Стратегии хранения данных

Существует несколько подходов к организации данных в мультидоменном проекте:

1. Общая база данных с полем site_id

Самый простой подход — добавить поле site_id во все таблицы:

-- Пример структуры таблицы товаров
CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    site_id VARCHAR(10) NOT NULL,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    price DECIMAL(10,2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_site_id (site_id),
    INDEX idx_site_name (site_id, name)
);

-- Пример запроса для конкретного сайта
SELECT * FROM products 
WHERE site_id = 'spb' 
AND active = 1 
ORDER BY created_at DESC;

Плюсы: простота реализации, одна база данных, легкое резервное копирование.

Минусы: риск "протекания" данных между сайтами, сложности с производительностью при росте.

2. Отдельные базы данных

Для каждого сайта своя база данных. Я использую этот подход для проектов с высокими требованиями к безопасности:

<?php
class DatabaseManager 
{
    private $connections = [];
    private $configs = [
        'main' => [
            'host' => 'localhost',
            'dbname' => 'example_main',
            'user' => 'user_main',
            'password' => 'pass_main'
        ],
        'spb' => [
            'host' => 'localhost',
            'dbname' => 'example_spb', 
            'user' => 'user_spb',
            'password' => 'pass_spb'
        ]
    ];
    
    public function getConnection($siteId) 
    {
        if (!isset($this->connections[$siteId])) {
            $config = $this->configs[$siteId] ?? $this->configs['main'];
            
            $dsn = "mysql:host={$config['host']};dbname={$config['dbname']};charset=utf8mb4";
            $this->connections[$siteId] = new PDO(
                $dsn, 
                $config['user'], 
                $config['password'],
                [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
                ]
            );
        }
        
        return $this->connections[$siteId];
    }
}
?>

3. Гибридный подход

Общие данные (пользователи, настройки) в одной базе, специфичный контент — в отдельных. На практике это самое удобное решение для средних проектов.

ℹ️
Из опыта: Начинайте с первого подхода (site_id), а при необходимости мигрируйте на отдельные базы. Преждевременная оптимизация — зло.

Управление контентом

Контент может быть общим для всех сайтов, уникальным для каждого, или частично пересекающимся. Я обычно создаю систему наследования:

<?php
class ContentManager 
{
    private $siteId;
    private $db;
    
    public function __construct($siteId, $db) 
    {
        $this->siteId = $siteId;
        $this->db = $db;
    }
    
    public function getPage($slug) 
    {
        // Сначала ищем страницу для конкретного сайта
        $page = $this->db->prepare("
            SELECT * FROM pages 
            WHERE slug = ? AND (site_id = ? OR site_id = 'global') 
            ORDER BY site_id DESC 
            LIMIT 1
        ");
        $page->execute([$slug, $this->siteId]);
        
        return $page->fetch();
    }
    
    public function getProducts($category = null) 
    {
        $sql = "
            SELECT p.*, pc.name as category_name 
            FROM products p
            LEFT JOIN product_categories pc ON p.category_id = pc.id
            WHERE p.site_id IN (?, 'global') AND p.active = 1
        ";
        
        $params = [$this->siteId];
        
        if ($category) {
            $sql .= " AND pc.slug = ?";
            $params[] = $category;
        }
        
        $sql .= " ORDER BY p.sort_order, p.created_at DESC";
        
        $stmt = $this->db->prepare($sql);
        $stmt->execute($params);
        
        return $stmt->fetchAll();
    }
}
?>

Этот подход позволяет иметь общие товары/страницы для всех сайтов (site_id = 'global') и специфичные для конкретных доменов.

SSL-сертификаты и безопасность

Одна из самых болезненных тем в мультидоменных проектах — SSL-сертификаты. Я помню времена, когда за каждый дополнительный домен в сертификате брали отдельную плату. Сейчас с Let's Encrypt стало проще, но нюансы остались.

Типы SSL-сертификатов для мультидомена

Wildcard сертификаты

Покрывают все поддомены одного домена. Например, *.example.com защитит и spb.example.com, и moscow.example.com, и api.example.com.

# Получение wildcard сертификата через certbot
sudo certbot certonly --manual \
  --preferred-challenges=dns \
  --email admin@example.com \
  --server https://acme-v02.api.letsencrypt.org/directory \
  --agree-tos \
  -d *.example.com -d example.com

После выполнения команды certbot попросит добавить TXT-запись в DNS. Это нужно сделать обязательно, иначе проверка не пройдёт.

Multi-Domain (SAN) сертификаты

Покрывают несколько разных доменов в одном сертификате:

# Получение мультидоменного сертификата
sudo certbot certonly --webroot \
  -w /var/www/example.com \
  -d example.com \
  -d www.example.com \
  -d example.ru \
  -d www.example.ru \
  -d example-shop.com

Отдельные сертификаты

Иногда проще получить отдельный сертификат для каждого домена. Особенно если домены находятся на разных серверах или имеют разных владельцев DNS.

Автоматизация обновления сертификатов

У одного клиента было 12 доменов с отдельными сертификатами. Обновлять вручную — кошмар. Я написал скрипт для автоматизации:

#!/bin/bash
# /etc/cron.daily/renew-ssl-certs

DOMAINS=(
    "example.com"
    "example.ru" 
    "spb.example.com"
    "moscow.example.com"
)

LOG_FILE="/var/log/ssl-renewal.log"

echo "$(date): Starting SSL renewal check" >> $LOG_FILE

for domain in "${DOMAINS[@]}"
do
    echo "Checking $domain..." >> $LOG_FILE
    
    # Проверяем срок действия сертификата
    expiry_date=$(echo | openssl s_client -servername $domain -connect $domain:443 2>/dev/null | \
                  openssl x509 -noout -dates | grep notAfter | cut -d= -f2)
    
    expiry_timestamp=$(date -d "$expiry_date" +%s)
    current_timestamp=$(date +%s)
    days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))
    
    if [ $days_until_expiry -lt 30 ]; then
        echo "Certificate for $domain expires in $days_until_expiry days. Renewing..." >> $LOG_FILE
        
        certbot renew --cert-name $domain --quiet
        
        if [ $? -eq 0 ]; then
            echo "Successfully renewed certificate for $domain" >> $LOG_FILE
            systemctl reload nginx
        else
            echo "Failed to renew certificate for $domain" >> $LOG_FILE
        fi
    else
        echo "Certificate for $domain is valid for $days_until_expiry days" >> $LOG_FILE
    fi
done

echo "$(date): SSL renewal check completed" >> $LOG_FILE
⚠️
Важно: Всегда тестируйте обновление сертификатов в staging-окружении. Неправильная настройка может положить все сайты сразу.

SEO и индексация мультидоменных сайтов

SEO для мультидоменного сайта — это отдельная наука. Я видел проекты, которые теряли до 70% трафика из-за неправильной настройки. Основная проблема — дублированный контент и каннибализация запросов.

Стратегия контента

Первое правило — у каждого домена должна быть чёткая тематическая или географическая ниша. Нельзя размещать одинаковый контент на разных доменах без локализации.

Вот как я обычно структурирую контент:

У каждого региона должны быть уникальные:

Настройка robots.txt

Для каждого домена нужен свой robots.txt. Больше деталей есть в моей статье о настройке robots.txt. Вот базовый пример:

# robots.txt для основного домена
User-agent: *
Allow: /

# Запрещаем дублированные страницы
Disallow: /admin/
Disallow: /api/
Disallow: /*?print=1
Disallow: /*?utm_*

# Указываем sitemap
Sitemap: https://example.com/sitemap.xml

# robots.txt для регионального домена
User-agent: *
Allow: /

Disallow: /admin/
Disallow: /api/

# Важно: свой sitemap для каждого домена
Sitemap: https://spb.example.com/sitemap.xml

Canonical URL и hreflang

Самая частая ошибка — неправильная настройка canonical. Если у вас есть страницы с похожим контентом на разных доменах, нужно чётко указать главную версию:

<!-- На странице spb.example.com/services/ -->
<link rel="canonical" href="https://spb.example.com/services/" />

<!-- Если это перевод основной страницы -->
<link rel="alternate" hreflang="ru" href="https://example.ru/services/" />
<link rel="alternate" hreflang="uk" href="https://example.ua/services/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/services/" />

Для автоматизации я написал функцию генерации hreflang:

<?php
function generateHreflangTags($currentUrl, $siteConfig) 
{
    $hreflangs = [];
    
    foreach ($siteConfig['sites'] as $siteId => $config) {
        $lang = $config['language'];
        $domain = $config['domain'];
        
        // Заменяем домен в URL
        $localizedUrl = str_replace(
            parse_url($currentUrl, PHP_URL_HOST), 
            $domain, 
            $currentUrl
        );
        
        $hreflangs[] = sprintf(
            '<link rel="alternate" hreflang="%s" href="%s" />',
            htmlspecialchars($lang),
            htmlspecialchars($localizedUrl)
        );
    }
    
    return implode("\n", $hreflangs);
}
?>

Sitemap для мультидомена

У каждого домена должен быть свой sitemap.xml. Но я также создаю индексный sitemap на основном домене:

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <sitemap>
        <loc>https://example.com/sitemap.xml</loc>
        <lastmod>2026-01-15T10:00:00+00:00</lastmod>
    </sitemap>
    <sitemap>
        <loc>https://spb.example.com/sitemap.xml</loc>
        <lastmod>2026-01-15T10:00:00+00:00</lastmod>
    </sitemap>
    <sitemap>
        <loc>https://moscow.example.com/sitemap.xml</loc>
        <lastmod>2026-01-15T10:00:00+00:00</lastmod>
    </sitemap>
</sitemapindex>

Тестирование и мониторинг

Мультидоменный сайт — это в разы больше точек отказа. У меня был случай, когда из-за проблем с DNS один из 8 доменов перестал резолвиться, и мы узнали об этом только через 3 дня от клиентов.

Автоматическое тестирование

Я настраиваю автоматические проверки для всех доменов. Подробнее об этом писал в статье про автоматическое тестирование сайтов. Вот базовый скрипт для проверки доступности:

#!/bin/bash
# /usr/local/bin/check-multisite-health.sh

DOMAINS=(
    "https://example.com"
    "https://spb.example.com" 
    "https://moscow.example.com"
    "https://example.ru"
)

TELEGRAM_BOT_TOKEN="YOUR_BOT_TOKEN"
TELEGRAM_CHAT_ID="YOUR_CHAT_ID"
LOG_FILE="/var/log/multisite-health.log"

send_telegram_alert() {
    local message="$1"
    curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
         -d chat_id="${TELEGRAM_CHAT_ID}" \
         -d text="🚨 Multisite Alert: ${message}"
}

check_domain() {
    local domain="$1"
    local response_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$domain")
    local response_time=$(curl -s -o /dev/null -w "%{time_total}" --max-time 10 "$domain")
    
    echo "$(date): Checking $domain - HTTP $response_code, ${response_time}s" >> "$LOG_FILE"
    
    if [ "$response_code" != "200" ]; then
        local alert_message="Domain $domain returned HTTP $response_code"
        echo "$alert_message" >> "$LOG_FILE"
        send_telegram_alert "$alert_message"
        return 1
    fi
    
    # Проверяем время ответа (больше 5 секунд - проблема)
    if (( $(echo "$response_time > 5.0" | bc -l) )); then
        local alert_message="Domain $domain slow response: ${response_time}s"
        echo "$alert_message" >> "$LOG_FILE"
        send_telegram_alert "$alert_message"
    fi
    
    return 0
}

echo "$(date): Starting multisite health check" >> "$LOG_FILE"

failed_domains=()

for domain in "${DOMAINS[@]}"
do
    if ! check_domain "$domain"; then
        failed_domains+=("$domain")
    fi
    sleep 2
done

if [ ${#failed_domains[@]} -gt 0 ]; then
    echo "$(date): Health check completed with ${#failed_domains[@]} failures" >> "$LOG_FILE"
    exit 1
else
    echo "$(date): All domains healthy" >> "$LOG_FILE"
    exit 0
fi

Запускаю этот скрипт каждые 5 минут через cron:

*/5 * * * * /usr/local/bin/check-multisite-health.sh

Мониторинг SSL-сертификатов

Отдельно слежу за сроком действия SSL-сертификатов на всех доменах:

#!/bin/bash
# Проверка срока действия SSL-сертификатов

check_ssl_expiry() {
    local domain="$1"
    local days_threshold=30
    
    local expiry_date=$(echo | openssl s_client -servername "$domain" -connect "$domain:443" 2>/dev/null | \
                       openssl x509 -noout -dates | grep notAfter | cut -d= -f2)
    
    if [ -z "$expiry_date" ]; then
        echo "❌ $domain: Unable to retrieve SSL certificate"
        return 1
    fi
    
    local expiry_timestamp=$(date -d "$expiry_date" +%s)
    local current_timestamp=$(date +%s)
    local days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))
    
    if [ $days_until_expiry -lt $days_threshold ]; then
        echo "⚠️  $domain: SSL expires in $days_until_expiry days"
        return 1
    else
        echo "✅ $domain: SSL valid for $days_until_expiry days"
        return 0
    fi
}

for domain in "${DOMAINS[@]}"
do
    domain_clean=$(echo "$domain" | sed 's|https://||')
    check_ssl_expiry "$domain_clean"
done
💡
Совет: Используйте внешние сервисы мониторинга типа UptimeRobot или StatusCake как дополнительную проверку. Иногда проблемы с сетью могут дать ложные срабатывания ваших скриптов.

Типовые проблемы и решения

За годы работы с мультидоменными проектами я собрал целую коллекцию типовых проблем. Поделюсь самыми частыми и их решениями.

Проблемы с куками и сессиями

Одна из самых частых проблем — когда пользователь не может залогиниться на поддомене или теряет корзину при переходе между доменами.

Решение — правильная настройка domain для кук:

<?php
// Настройка домена для кук в PHP
function setMultidomainCookie($name, $value, $expire = 0) 
{
    $host = $_SERVER['HTTP_HOST'];
    
    // Определяем основной домен
    if (preg_match('/([^.]+\.[^.]+)$/', $host, $matches)) {
        $domain = '.' . $matches[1]; // .example.com
    } else {
        $domain = $host;
    }
    
    setcookie($name, $value, [
        'expires' => $expire,
        'path' => '/',
        'domain' => $domain,
        'secure' => isset($_SERVER['HTTPS']),
        'httponly' => true,
        'samesite' => 'Lax'
    ]);
}

// Для сессий
ini_set('session.cookie_domain', '.example.com');
ini_set('session.cookie_path', '/');
?>

Проблемы с CORS

Если у вас есть AJAX-запросы между доменами (например, API на api.example.com, а фронт на www.example.com), нужно настроить CORS:

<?php
// api.example.com/cors-handler.php
$allowedDomains = [
    'https://example.com',
    'https://www.example.com', 
    'https://spb.example.com',
    'https://moscow.example.com'
];

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

if (in_array($origin, $allowedDomains)) {
    header("Access-Control-Allow-Origin: $origin");
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
    header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
}

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    exit(0);
}
?>

Проблемы с редиректами

Частая ошибка — циклические редиректы между доменами. Особенно при использовании www и без www:

# Правильная настройка редиректов в Nginx
server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name www.example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    return 301 https://example.com$request_uri;
}

# Основной сервер
server {
    listen 443 ssl;
    server_name example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # Основная конфигурация сайта
}

Проблемы с производительностью

Мультидоменные сайты часто работают медленнее из-за дополнительных DNS-запросов и множественных SSL-рукопожатий. Несколько советов по оптимизации:

Больше деталей по оптимизации в моей статье почему сайт медленно работает.

Миграция и масштабирование

Рано или поздно мультидоменный проект нужно масштабировать или переносить. У меня был клиент, который начал с 3 региональных доменов, а через 2 года у него было 25 городов.

Планирование миграции

Когда мигрируете мультидоменный сайт, планируйте в 3 раза больше времени, чем для обычного. Каждый домен — отдельная точка отказа.

Мой чек-лист для миграции:

  1. Создать полную карту всех доменов и поддоменов
  2. Проверить все SSL-сертификаты
  3. Настроить мониторинг на новом сервере
  4. Подготовить план отката для каждого домена
  5. Мигрировать домены поэтапно, не все сразу

Подробнее о миграции писал в статье миграция сайта на новый хостинг.

Автоматизация развёртывания

Для мультидоменных проектов автоматизация критически важна. Вот базовый скрипт деплоя, который я использую:

#!/bin/bash
# deploy-multisite.sh

SITES=(
    "example.com:/var/www/example.com"
    "spb.example.com:/var/www/example.com"
    "moscow.example.com:/var/www/example.com"
    "example.ru:/var/www/example.ru"
)

GIT_REPO="git@github.com:username/multisite-project.git"
BACKUP_DIR="/backups/$(date +%Y%m%d_%H%M%S)"

echo "Starting multisite deployment..."

# Создаём бэкап
mkdir -p "$BACKUP_DIR"
for site_info in "${SITES[@]}"; do
    domain=$(echo "$site_info" | cut -d: -f1)
    path=$(echo "$site_info" | cut -d: -f2)
    
    echo "Backing up $domain..."
    tar -czf "$BACKUP_DIR/$domain.tar.gz" -C "$path" .
done

# Обновляем код
echo "Pulling latest code..."
cd /tmp
rm -rf multisite-deploy
git clone "$GIT_REPO" multisite-deploy
cd multisite-deploy

# Деплоим на каждый сайт
for site_info in "${SITES[@]}"; do
    domain=$(echo "$site_info" | cut -d: -f1)
    path=$(echo "$site_info" | cut -d: -f2)
    
    echo "Deploying to $domain ($path)..."
    
    # Проверяем, что сайт работает перед деплоем
    if ! curl -f -s "$domain" > /dev/null; then
        echo "⚠️  $domain is not responding, skipping..."
        continue
    fi
    
    # Копируем файлы
    rsync -av --exclude='.git' --exclude='node_modules' ./ "$path/"
    
    # Устанавливаем права
    chown -R www-data:www-data "$path"
    
    # Проверяем работоспособность после деплоя
    sleep 5
    if curl -f -s "$domain" > /dev/null; then
        echo "✅ $domain deployed successfully"
    else
        echo "❌ $domain deployment failed, rolling back..."
        tar -xzf "$BACKUP_DIR/$domain.tar.gz" -C "$path"
    fi
done

echo "Deployment completed!"

Масштабирование инфраструктуры

Когда доменов становится много, одного сервера может не хватить. Я обычно использую такую схему:

Подробнее о настройке CDN в статье как настроить CDN для сайта.

Настройка мультидоменного сайта — это сложная задача, которая требует глубокого понимания веб-технологий. Но при правильном подходе это мощный инструмент для развития бизнеса. Главное — не пытаться сделать всё сразу. Начните с простой схемы, протестируйте её, а затем постепенно усложняйте.

Если у вас есть вопросы по настройке мультидоменного сайта или нужна помощь с реализацией, обращайтесь за поддержкой Битрикс или доработкой сайта. Я помогу настроить всё правильно с первого раза.

Нужна помощь с настройкой мультидоменного сайта?

Наши эксперты помогут правильно настроить мультидоменную структуру с учетом SEO и технических требований.

П
Павел
Веб-разработчик · 10+ лет опыта · Bitrix, WordPress, Laravel

Читайте также

Доработка сайта: что можно улучшить Как перенести сайт на другую CMS Настройка кеширования в Битрикс: полное руководство