В чем разница между HTTP_HOST и SERVER_NAME в PHP? - PullRequest
513 голосов
/ 19 февраля 2010

Когда вы рассматриваете возможность использования одного над другим и почему?

Ответы [ 9 ]

760 голосов
/ 19 февраля 2010

HTTP_HOST получается из заголовка HTTP-запроса , и именно это клиент фактически использовал в качестве "целевого хоста" запроса. SERVER_NAME определяется в конфигурации сервера. Какой из них использовать, зависит от того, для чего он вам нужен. Однако теперь вы должны понимать, что одно является контролируемым клиентом значением, которое, таким образом, может быть ненадежным для использования в бизнес-логике, а другое - управляемым сервером значением, которое является более надежным. Однако вам необходимо убедиться, что на данном веб-сервере правильно настроен SERVER_NAME. Взяв Apache HTTPD в качестве примера, вот выдержка из его документации :

Если не указано ServerName, то сервер пытается определить имя хоста, выполнив обратный поиск по IP-адресу. Если в ServerName порт не указан, то сервер будет использовать порт из входящего запроса. Для оптимальной надежности и предсказуемости вы должны указать явное имя хоста и порт с помощью директивы ServerName.


Обновление : после проверки ответа Пекки на ваш вопрос , который содержит ссылку на ответ Бобинса , что PHP всегда будет возвращать значение HTTP_HOST для SERVER_NAME, что противоречит моему собственному опыту работы с PHP 4.x + Apache HTTPD 1.2.x, полученному пару лет назад, я немного стряхнул с себя свою нынешнюю среду XAMPP в Windows XP (Apache HTTPD 2.2.1 с PHP 5.2. 8), запустил его, создал страницу PHP, которая печатает оба значения, создал тестовое приложение Java, используя URLConnection для изменения заголовка Host, и тесты показали, что это действительно (неправильно) случай.

Сначала подозревая PHP и копаясь в некоторых сообщениях об ошибках PHP по теме, я узнал, что корень проблемы в используемом веб-сервере, что он неправильно возвращает заголовок HTTP Host, когда SERVER_NAME был запрошен Поэтому я копался в отчетах об ошибках Apache HTTPD , используя различные ключевые слова относительно предмета, и наконец нашел связанную ошибку . Такое поведение было введено начиная с Apache HTTPD 1.3. Вам необходимо установить директиву UseCanonicalName на on в <VirtualHost> записи ServerName в httpd.conf (также проверьте предупреждение внизу документа !).

<VirtualHost *>
    ServerName example.com
    UseCanonicalName on
</VirtualHost> 

Это сработало для меня.

Подводя итог, SERVER_NAME более надежен, но вы зависите от конфигурации сервера!

63 голосов
/ 19 февраля 2010

HTTP_HOST - целевой хост, отправленный клиентом.Это может свободно управляться пользователем.Нет проблем, если вы отправите запрос на ваш сайт с просьбой указать значение HTTP_HOST www.stackoverflow.com.

SERVER_NAME из определения сервера VirtualHost и поэтому считается более надежным.Однако им также можно манипулировать извне при определенных условиях, связанных с настройкой вашего веб-сервера: см. Этот вопрос SO , касающийся аспектов безопасности обоих вариантов.

Вы не должны полагаться ни на то, чтобы быть в безопасности.Тем не менее, что использовать на самом деле зависит от того, что вы хотите сделать.Если вы хотите определить, в каком домене работает ваш скрипт, вы можете безопасно использовать HTTP_HOST, если недопустимые значения, исходящие от злонамеренного пользователя, не могут ничего сломать.

51 голосов
/ 20 августа 2013

Как я уже упоминал в в этом ответе , если сервер работает не на 80 порте (как это обычно бывает на компьютере для разработки / интрасети), тогда HTTP_HOST содержит порт, а SERVER_NAME не.

$_SERVER['HTTP_HOST'] == 'localhost:8080'
$_SERVER['SERVER_NAME'] == 'localhost'

(По крайней мере, это то, что я заметил в виртуальных хостах на основе портов Apache)

Обратите внимание, что HTTP_HOST не не содержит :443 при работе по HTTPS (если вы не используете нестандартный порт, который я не тестировал).

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

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
26 голосов
/ 27 февраля 2012

Обратите внимание, что если вы хотите использовать IPv6, вы, вероятно, хотите использовать HTTP_HOST вместо SERVER_NAME.Если вы введете http://[::1]/, переменные среды будут следующими:

HTTP_HOST = [::1]
SERVER_NAME = ::1

Это означает, что если вы, например, выполните mod_rewrite, вы можете получить неприятный результат.Пример для перенаправления SSL:

# SERVER_NAME will NOT work - Redirection to https://::1/
RewriteRule .* https://%{SERVER_NAME}/

# HTTP_HOST will work - Redirection to https://[::1]/
RewriteRule .* https://%{HTTP_HOST}/

Это применимо ТОЛЬКО, если вы обращаетесь к серверу без имени хоста.

6 голосов
/ 24 мая 2010

, если вы хотите проверить через server.php или как-нибудь еще, чтобы вызвать его со следующим:

<?php

phpinfo(INFO_VARIABLES);

?>

или

<?php

header("Content-type: text/plain");

print_r($_SERVER);

?>

Затем получите доступ ко всемдействительные URL для вашего сайта и проверьте разницу.

4 голосов
/ 19 февраля 2010

Зависит от того, что я хочу выяснить. SERVER_NAME - это имя хоста сервера, а HTTP_HOST - это виртуальный хост, к которому подключен клиент.

2 голосов
/ 09 марта 2017

Предполагается, что у вас простая установка (CentOS 7, Apache 2.4.x и PHP 5.6.20) и только один веб-сайт (без виртуального хостинга) ...

В смысле PHP, $_SERVER['SERVER_NAME'] это элемент, который PHP регистрирует в суперглобальном $_SERVER на основе вашей конфигурации Apache (директива **ServerName** с UseCanonicalName On) в httpd.conf (будь то из включенного файла конфигурации виртуального хоста, что угодно, и т. д.). HTTP_HOST выводится из заголовка HTTP host.Относитесь к этому как к пользовательскому вводу.Фильтруйте и проверяйте перед использованием.

Вот пример, где я использую $_SERVER['SERVER_NAME'] в качестве основы для сравнения.Следующий метод взят из конкретного дочернего класса, который я создал с именем ServerValidator (потомок Validator).ServerValidator проверяет шесть или семь элементов в $ _SERVER перед их использованием.

При определении, является ли HTTP-запрос POST, я использую этот метод.

public function isPOST()
{
    return (($this->requestMethod === 'POST')    &&  // Ignore
            $this->hasTokenTimeLeft()            &&  // Ignore
            $this->hasSameGETandPOSTIdentities() &&  // Ingore
            ($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')));
}

К тому времени, когда этот методвызвано, все фильтрация и проверка соответствующих элементов $ _SERVER произошли бы (и соответствующие свойства были установлены).

Строка ...

($this->httpHost === filter_input(INPUT_SERVER, 'SERVER_NAME')

... проверяет, что $_SERVER['HTTP_HOST']значение (в конечном счете полученное из запрошенного host заголовка HTTP) соответствует $_SERVER['SERVER_NAME'].

Теперь я использую superglobal talk для объяснения моего примера, но это только потому, что некоторые люди не знакомы с INPUT_GET, INPUT_POST и INPUT_SERVER в отношении filter_input_array().

Суть в том, что я не обрабатываю POST-запросы на моем сервере, если all четыре условия выполнены.Следовательно, с точки зрения запросов POST, неспособность предоставить заголовок HTTP host (наличие проверено ранее) заклинания doom для строгих HTTP 1.0 браузеров.Кроме того, запрашиваемый хост должен соответствовать значению для ServerName в httpd.conf и, в качестве расширения, значению $_SERVER('SERVER_NAME') в$_SERVER суперглобальный.Опять же, я буду использовать INPUT_SERVER с функциями фильтра PHP, но вы поймете мой дрейф.

Имейте в виду, что Apache часто использует ServerName в стандартных перенаправлениях (например, при выходекосая черта в URL: пример, http://www.foo.com становится http://www.foo.com/),, даже если вы не используете перезапись URL.

Я использую $_SERVER['SERVER_NAME'] в качествестандарт, а не $_SERVER['HTTP_HOST']. Существует много вопросов по этому вопросу. $_SERVER['HTTP_HOST'] может быть пустым, так что это не должно быть основой для создания соглашений кода, таких как мой открытый метод выше. Но только потому, что оба могутбыть установленным не гарантирует, что они будут равны. Тестирование - лучший способ узнать наверняка (с учетом версии Apache и версии PHP).

2 голосов
/ 17 февраля 2012

Мне потребовалось некоторое время, чтобы понять, что люди подразумевают под «SERVER_NAME, более надежным». Я использую общий сервер и не имею доступа к директивам виртуального хоста. Итак, я использую mod_rewrite в .htaccess для сопоставления разных HTTP_HOST с разными каталогами. В этом случае значение имеет HTTP_HOST.

Ситуация аналогична, если использовать виртуальные хосты на основе имен: директива ServerName внутри виртуального хоста просто говорит, какое имя хоста будет сопоставлено с этим виртуальным хостом. Суть в том, что в обоих случаях имя хоста, предоставленное клиентом во время запроса (HTTP_HOST), должно совпадать с именем на сервере, которое само сопоставлено с каталогом. Независимо от того, выполняется ли сопоставление с директивами виртуального хоста или с правилами htaccess mod_rewrite, здесь второстепенно. В этих случаях HTTP_HOST будет таким же, как SERVER_NAME. Я рад, что Apache настроен таким образом.

Однако с виртуальными хостами на базе IP ситуация иная. В этом и только в этом случае SERVER_NAME и HTTP_HOST могут различаться, поскольку теперь клиент выбирает сервер по IP, а не по имени. Действительно, могут существовать специальные конфигурации, где это важно .

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

0 голосов
/ 20 мая 2016

Как сказал balusC, имя SERVER_NAME ненадежно и может быть изменено в конфигурации apache, конфигурации имени сервера сервера и брандмауэра, которые могут находиться между вами и сервером.

Следующая функция всегда возвращает реальный хост (хост, введенный пользователем) без порта, и это почти надежно:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...