В чем разница между HTTP_HOST и SERVER_NAME в PHP?
Когда вы рассматриваете возможность использования одного над другим и почему?
11 ответов
HTTP_HOST
получается из заголовка HTTP-запроса, и это то, что клиент фактически использовал в качестве "целевого хоста" запроса. SERVER_NAME
определяется в конфигурации сервера. Какой из них использовать, зависит от того, для чего он вам нужен. Однако теперь вы должны понимать, что одно является контролируемым клиентом значением, которое, таким образом, может быть ненадежным для использования в бизнес-логике, а другое - управляемым сервером значением, которое является более надежным. Однако вы должны убедиться, что рассматриваемый веб-сервер имеет SERVER_NAME
правильно настроен. Взяв Apache HTTPD в качестве примера, вот выдержка из его документации:
Если нет
ServerName
затем сервер пытается определить имя хоста, выполнив обратный поиск по IP-адресу. Если порт не указан вServerName
, то сервер будет использовать порт из входящего запроса. Для оптимальной надежности и предсказуемости вы должны указать явное имя хоста и порт, используяServerName
директивы.
Обновление: после проверки ответа Pekka на ваш вопрос, который содержит ссылку на ответ bobince, 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.stackru.com
,
SERVER_NAME
исходит от сервера VirtualHost
определение и, следовательно, считается более надежным. Тем не менее, им также можно манипулировать извне при определенных условиях, связанных с настройкой вашего веб-сервера: см. Этот вопрос SO, в котором рассматриваются аспекты безопасности обоих вариантов.
Вы не должны полагаться ни на то, чтобы быть в безопасности. Тем не менее, что использовать на самом деле зависит от того, что вы хотите сделать. Если вы хотите определить, на каком домене работает ваш скрипт, вы можете смело использовать 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 для вашего сайта и проверьте разницу.
Зависит от того, что я хочу узнать. SERVER_NAME - это имя хоста сервера, а HTTP_HOST - это виртуальный хост, к которому подключен клиент.
$_SERVER['SERVER_NAME'] основан на конфигурации ваших веб-серверов.$_SERVER['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']
значение (в конечном итоге получено из запрошенного host
HTTP заголовок) совпадения $_SERVER['SERVER_NAME']
,
Теперь я использую суперглобальную речь, чтобы объяснить мой пример, но это только потому, что некоторые люди не знакомы с INPUT_GET
, INPUT_POST
, а также INPUT_SERVER
в отношении filter_input_array()
,
Суть в том, что я не обрабатываю POST-запросы на моем сервере, если не выполнены все четыре условия. Следовательно, с точки зрения запросов POST, неспособность предоставить HTTP host
Заголовок (наличие проверено ранее) означает гибель для строгих браузеров 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).
Я недоволен всеми ответами. Некоторые из них верны, но не рассказывают всей истории и не проясняют проблему.
Независимо от того, какой http-сервер вы используете, в заголовке HTTP должно содержаться исходное значение, отправленное клиентом. И, следовательно, это данные, контролируемые пользователем, которым нельзя доверять.
Эта переменная настроена в конфигурации вашего сервера и может привести к неправильному URL-адресу. Например, перед вашими веб-серверами может быть обратный прокси-сервер иSERVER_NAME
есть иserver2
но вы не хотите перенаправлять своего пользователя наserver1
но для удобного хоста.
Таким образом,HTTP_HOST
является более надежной переменной, так как вы можете захотеть, чтобы хост, запрошенный клиентом, достиг вашего приложения PHP. Вам не нужно сравнивать их, чтобы убедиться, что это допустимое значение (они не обязательно равны). Есть два способа убедиться, что это значение допустимо:
- сравните значение со списком допустимых значений (вам нужно знать допустимые значения)
- убедитесь, что ваш веб-сервер возвращает ошибку, если значение неверно
Первый из них прост для понимания, но может быть проблематичным в реальных сценариях (в вашей среде разработки это то, при постановке это то, при производстве это... и т. д.). Это означает, что вам нужно знать в PHP, что допустимо для этой среды.
Второй — что-то для конфигурации сервера: VirtualHost — это концепция HTTP-серверов для доставки нескольких веб-сайтов с одного сервера. Поскольку виртуальный хост выбирается по заголовку httpHost
(без учета регистра) клиент не может контролировать, какой виртуальный хост используется, если не изменить хост. Когда настроен только один виртуальный хост, каждое значение будет использовать этот виртуальный хост. Вам необходимо настроить второй виртуальный хост, который будет использоваться по умолчанию (если нет других подходящих виртуальных хостов) и всегда будет возвращать ошибку (например, «не найден» или «запрещен»).
Как говорит balusC, имя SERVER_NAME ненадежно и может быть изменено в конфигурации apache, конфигурации имени сервера сервера и брандмауэра, которые могут находиться между вами и сервером.
Следующая функция всегда возвращает реальный хост (введенный пользователем хост) без порта, и это почти надежно:
function getRealHost(){
list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
return $realHost;
}