Avatar ·

В чём разница между HTTP_HOST и SERVER_NAME в PHP? Правильное использование

Всем привет! Я новичок в PHP и пытаюсь разобраться с серверными переменными. Часто вижу в коде других разработчиков использование `$_SERVER['HTTP_HOST']` и `$_SERVER['SERVER_NAME']`. На первый взгляд они оба возвращают имя хоста, но я заметил, что иногда они выдают разные значения. Например, у меня есть простой скрипт: ```php '; echo 'SERVER_NAME: ' . $_SERVER['SERVER_NAME'] . '
'; ?> ``` На локальном сервере (OpenServer) оба показывают `localhost`. Но когда я залил сайт на хостинг, то заметил, что при обращении через IP-адрес `HTTP_HOST` показывает IP, а `SERVER_NAME` — доменное имя. А ещё я слышал, что `HTTP_HOST` может быть подделан через заголовки HTTP-запроса, и это создаёт уязвимость. Подскажите, пожалуйста: 1. В чём принципиальная разница между этими переменными? 2. Какую из них безопаснее использовать для редиректов, генерации ссылок и проверки домена? 3. Есть ли случаи, когда они могут кардинально отличаться? Буду благодарен за подробное объяснение с примерами! Я хочу понять не просто "как работает", а "почему так сделано" и какой вариант правильнее в разных ситуациях. <<>>

Ключевое различие: источник данных

Главное отличие кроется в том, откуда берётся каждое значение:

  • HTTP_HOST — берётся из HTTP-заголовка Host, который клиент (браузер, curl, бот) отправляет в запросе. Это значение может быть любым — клиент сам решает, что отправить.
  • SERVER_NAME — задаётся конфигурацией веб-сервера (Apache/Nginx). Обычно это значение из директивы ServerName в Apache или server_name в Nginx.

Когда они совпадают?

В 95% случаев для обычного сайта, когда пользователь заходит по домену example.com, оба значения будут одинаковыми. Но есть важные исключения:

Сценарии расхождения

1. Обращение по IP-адресу

// Запрос: http://192.168.1.1/site.php
$_SERVER['HTTP_HOST'] = '192.168.1.1'; // то, что ввел пользователь
$_SERVER['SERVER_NAME'] = 'example.com'; // то, что настроено в сервере

2. Виртуальные хосты (VirtualHost)

// Apache config:
<VirtualHost *:80>
    ServerName site1.com
    ServerAlias site2.com
</VirtualHost>

// Запрос к site2.com:
$_SERVER['HTTP_HOST'] = 'site2.com'; // из заголовка
$_SERVER['SERVER_NAME'] = 'site1.com'; // из ServerName (основной)

3. Подмена заголовка (HTTP Host header attack)

// Злоумышленник отправляет:
curl -H "Host: evil.com" http://example.com/

$_SERVER['HTTP_HOST'] = 'evil.com'; // подделано!
$_SERVER['SERVER_NAME'] = 'example.com'; // настоящий домен

Какую переменную использовать?

Для редиректов и генерации абсолютных ссылок — HTTP_HOST

// Правильно:
$url = 'https://' . $_SERVER['HTTP_HOST'] . '/page';
header("Location: $url");

// Потому что пользователь видит именно этот домен в адресной строке

Для проверки легитимности домена — SERVER_NAME

// Безопасная проверка:
$allowed_domains = ['example.com', 'example.org'];
if (!in_array($_SERVER['SERVER_NAME'], $allowed_domains)) {
    die('Доступ запрещён');
}

// HTTP_HOST здесь использовать опасно!

Практические рекомендации

  1. Для вывода на экран — используйте HTTP_HOST. Пользователь должен видеть именно тот адрес, который он ввёл.
  2. Для аутентификации и критичных проверок — используйте SERVER_NAME. Он надёжнее, так как не подделывается.
  3. В CMS и фреймворках часто используют HTTP_HOST для определения текущего домена, но с дополнительной валидацией через белый список.
  4. В конфигурации веб-сервера всегда явно указывайте ServerName — это повышает безопасность.

Пример правильной обработки

function getCurrentDomain() {
    // Проверяем, что HTTP_HOST не подделан
    if (isset($_SERVER['HTTP_HOST'])) {
        $host = $_SERVER['HTTP_HOST'];
        // Очищаем от потенциально опасных символов
        $host = strtolower(trim($host));
        $host = filter_var($host, FILTER_SANITIZE_URL);
        return $host;
    }
    return $_SERVER['SERVER_NAME'];
}

// Использование:
$domain = getCurrentDomain();
$url = "https://{$domain}/dashboard";

Итог: HTTP_HOST — это то, что хочет показать пользователь, SERVER_NAME — то, что настроил администратор. Для публичного контента используйте HTTP_HOST, для безопасности — SERVER_NAME.

<<>>

Дополнение: особенности в разных веб-серверах

Стоит отметить, что поведение SERVER_NAME может различаться в зависимости от настроек веб-сервера:

  • Apache с UseCanonicalName On (по умолчанию) — SERVER_NAME равен значению из директивы ServerName. Поведение предсказуемое.
  • Apache с UseCanonicalName Off — SERVER_NAME может совпадать с HTTP_HOST, то есть браться из заголовка запроса. Это снижает безопасность.
  • Nginx — SERVER_NAME всегда равен значению из конфигурации server_name, если не используется проксирование.

Прокси и балансировщики

Если сайт стоит за обратным прокси (например, Cloudflare, nginx как прокси для Apache), то ситуация усложняется:

// Клиент -> Cloudflare -> Ваш сервер
$_SERVER['HTTP_HOST'] = 'example.com'; // оригинальный домен
$_SERVER['SERVER_NAME'] = '127.0.0.1'; // внутренний IP сервера!

В таких случаях нужно смотреть на заголовок X-Forwarded-Host:

// Правильно за прокси:
$host = $_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'];

Совет: всегда проверяйте, как ваша инфраструктура обрабатывает эти переменные. Для продакшена лучше явно задавать домен в конфигурационном файле приложения, а не полагаться на серверные переменные.