В чем разница между HTTP-ХОСТОМ и ИМЕНЕМ СЕРВЕРА в PHP?
Когда бы вы подумали об использовании одного над другим и почему?
9 answers
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
более надежен, но вы зависите от конфигурации сервера!
HTTP_HOST
является целевым хостом, отправленным клиентом. Пользователь может свободно манипулировать им. Нет проблем отправить запрос на ваш сайт с запросом значения HTTP_HOST
www.stackoverflow.com
.
SERVER_NAME
исходит из определения сервера VirtualHost
и поэтому считается более надежным. Однако им также можно управлять извне при определенных условиях, связанных с настройкой вашего веб-сервера: см. Это Это ТАК вопрос это касается аспектов безопасности обоих вариации.
Вы не должны полагаться ни на то, ни на другое, чтобы быть в безопасности. Тем не менее, то, что использовать, действительно зависит от того, что вы хотите сделать. Если вы хотите определить, в каком домене выполняется ваш скрипт, вы можете безопасно использовать HTTP_HOST
, если недопустимые значения, поступающие от злоумышленника, ничего не могут нарушить.
Как я уже упоминал в этом ответе , если сервер работает на порту, отличном от 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'
Пожалуйста, обратите внимание, что если вы хотите использовать 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}/
Это применимо ТОЛЬКО в том случае, если вы обращаетесь к серверу без имени хоста.
Если вы хотите проверить через server.php или как бы вы ни хотели это назвать, используя следующее:
<?php
phpinfo(INFO_VARIABLES);
?>
Или
<?php
header("Content-type: text/plain");
print_r($_SERVER);
?>
Затем откройте его со всеми действительными URL-адресами для вашего сайта и проверьте разницу.
Зависит от того, что я хочу выяснить. ИМЯ_СЕРВЕРА - это имя хоста сервера, в то время как HTTP_HOST - это виртуальный хост, к которому подключен клиент.
Мне потребовалось некоторое время, чтобы понять, что люди подразумевают под "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
, на всякий случай, если мой код будет перенесен в эти специальные конфигурации.
Предполагая, что у вас простая настройка (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']
(в конечном счете полученное из запрошенного заголовка HTTP host
) совпадает $_SERVER['SERVER_NAME']
.
Теперь я использую суперглобальный язык, чтобы объяснить свой пример, но это только потому, что некоторые люди не знакомы с INPUT_GET
, INPUT_POST
, и INPUT_SERVER
в отношении filter_input_array()
.
Суть в том, что я не обрабатываю запросы 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).
Как сказал балусК, ИМЯ_СЕРВЕРА ненадежно и может быть изменено в конфигурации apache, конфигурации имени сервера сервера и брандмауэра, который может быть между вами и сервером.
Следующая функция всегда возвращает реальный хост (хост, введенный пользователем) без порта, и это почти надежно:
function getRealHost(){
list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
return $realHost;
}