За 10 лет работы с веб-проектами я настроил десятки мультидоменных сайтов — от простых корпоративных порталов до сложных интернет-магазинов с региональными версиями. И честно говоря, многие разработчики до сих пор делают критические ошибки в этой области.
Что такое мультидоменный сайт и зачем он нужен
Мультидоменный сайт — это веб-ресурс, который работает на нескольких доменах одновременно. Например, основной сайт на example.com, а его региональные версии на spb.example.com, moscow.example.com или отдельные домены типа example.ru, example.ua.
На моей практике чаще всего встречаются такие сценарии:
- Региональные версии одного бизнеса (франшизы, филиалы)
- Разные языковые версии сайта
- Отдельные домены для мобильной версии (хотя это уже архаизм)
- Тестовые и staging-окружения
- Белые метки (white label) для партнёров
У одного моего клиента была сеть автосалонов в 15 городах. Каждый город требовал отдельного домена с локальным контентом, но при этом единой системы управления. Именно тогда я понял, насколько важно правильно спроектировать архитектуру с самого начала.
Основные преимущества мультидоменного подхода:
- Лучшая геотаргетинг и локализация
- Возможность разного дизайна для разных аудиторий
- Гибкость в SEO-продвижении
- Масштабируемость бизнеса
Но есть и минусы — сложность настройки, дублирование контента, проблемы с SSL-сертификатами и увеличенные расходы на хостинг.
Виды мультидоменных настроек
За годы практики я выделил несколько основных архитектурных подходов к мультидоменным сайтам. Каждый имеет свои особенности и область применения.
Поддомены (субдомены)
Самый простой вариант — использование поддоменов типа city.example.com. Я часто рекомендую именно этот подход для начинающих проектов. Настройка происходит на уровне DNS и веб-сервера, без необходимости регистрировать дополнительные домены.
Плюсы:
- Один SSL-сертификат wildcard покрывает все поддомены
- Проще настройка CORS и куки между доменами
- Меньше затрат на домены
Минусы:
- Ограничения для некоторых геодоменов (.ru, .ua)
- Меньший вес в SEO по сравнению с отдельными доменами
Отдельные домены
Полностью независимые домены — 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. Гибридный подход
Общие данные (пользователи, настройки) в одной базе, специфичный контент — в отдельных. На практике это самое удобное решение для средних проектов.
Управление контентом
Контент может быть общим для всех сайтов, уникальным для каждого, или частично пересекающимся. Я обычно создаю систему наследования:
<?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
SEO и индексация мультидоменных сайтов
SEO для мультидоменного сайта — это отдельная наука. Я видел проекты, которые теряли до 70% трафика из-за неправильной настройки. Основная проблема — дублированный контент и каннибализация запросов.
Стратегия контента
Первое правило — у каждого домена должна быть чёткая тематическая или географическая ниша. Нельзя размещать одинаковый контент на разных доменах без локализации.
Вот как я обычно структурирую контент:
- example.com — основной сайт, федеральные запросы
- spb.example.com — "услуги в Санкт-Петербурге", местные новости
- moscow.example.com — "услуги в Москве", региональные кейсы
У каждого региона должны быть уникальные:
- Title и Description
- H1 заголовки
- Контактные данные
- Отзывы и кейсы
- Цены (если есть региональные различия)
Настройка 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
Типовые проблемы и решения
За годы работы с мультидоменными проектами я собрал целую коллекцию типовых проблем. Поделюсь самыми частыми и их решениями.
Проблемы с куками и сессиями
Одна из самых частых проблем — когда пользователь не может залогиниться на поддомене или теряет корзину при переходе между доменами.
Решение — правильная настройка 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-рукопожатий. Несколько советов по оптимизации:
- Используйте HTTP/2 для всех доменов
- Настройте prefetch для часто используемых поддоменов
- Минимизируйте количество внешних ресурсов
- Используйте CDN для статики
Больше деталей по оптимизации в моей статье почему сайт медленно работает.
Миграция и масштабирование
Рано или поздно мультидоменный проект нужно масштабировать или переносить. У меня был клиент, который начал с 3 региональных доменов, а через 2 года у него было 25 городов.
Планирование миграции
Когда мигрируете мультидоменный сайт, планируйте в 3 раза больше времени, чем для обычного. Каждый домен — отдельная точка отказа.
Мой чек-лист для миграции:
- Создать полную карту всех доменов и поддоменов
- Проверить все SSL-сертификаты
- Настроить мониторинг на новом сервере
- Подготовить план отката для каждого домена
- Мигрировать домены поэтапно, не все сразу
Подробнее о миграции писал в статье миграция сайта на новый хостинг.
Автоматизация развёртывания
Для мультидоменных проектов автоматизация критически важна. Вот базовый скрипт деплоя, который я использую:
#!/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!"
Масштабирование инфраструктуры
Когда доменов становится много, одного сервера может не хватить. Я обычно использую такую схему:
- Load Balancer — распределяет нагрузку между серверами
- Web-серверы — несколько идентичных серверов с сайтами
- Database Master/Slave — основная БД и реплики для чтения
- Redis/Memcached — кеширование сессий и данных
- CDN — для статических файлов
Подробнее о настройке CDN в статье как настроить CDN для сайта.
Настройка мультидоменного сайта — это сложная задача, которая требует глубокого понимания веб-технологий. Но при правильном подходе это мощный инструмент для развития бизнеса. Главное — не пытаться сделать всё сразу. Начните с простой схемы, протестируйте её, а затем постепенно усложняйте.
Если у вас есть вопросы по настройке мультидоменного сайта или нужна помощь с реализацией, обращайтесь за поддержкой Битрикс или доработкой сайта. Я помогу настроить всё правильно с первого раза.
Нужна помощь с настройкой мультидоменного сайта?
Наши эксперты помогут правильно настроить мультидоменную структуру с учетом SEO и технических требований.