Qual é a diferença entre HTTP_HOST e SERVER_NAME no PHP?

533

Qual é a diferença entre HTTP_HOSTe SERVER_NAMEno PHP?

Onde:

  • HTTP_POST === $_SERVER['HTTP_HOST']
  • SERVER_NAME === $_SERVER['SERVER_NAME']

Quando você consideraria usar um sobre o outro e por quê?

Emanuil Rusev
fonte
14
"Eu normalmente uso HTTP_HOST, para que o usuário permaneça no nome exato do host em que começou. Por exemplo, se eu tiver o mesmo site nos domínios .com e .org, não quero enviar alguém de .org para .com, principalmente se eles tiverem tokens de login no .org que perderiam se enviados para outro domínio. " - Este e alguns outros pontos interessantes de stackoverflow.com/questions/1459739/…
Yarin
5
@ Yarin, Não se esqueça de verificar os resultados da lista de permissõesHTTP_HOST . Caso contrário, um invasor pode atribuir qualquer valor à Host:solicitação do HTTP e fazer com que o servidor a aceite.
Pacerier
6
Iniciantes: Esta questão é referente a valores tipicamente obtidos através $_SERVER['HTTP_HOST']ou$_SERVER['SERVER_NAME']
Gregory Cosmo Haun

Respostas:

780

O HTTP_HOSTé obtido no cabeçalho da solicitação HTTP e é isso que o cliente realmente usou como "host de destino" da solicitação. O SERVER_NAMEé definido na configuração do servidor. Qual deles usar depende do que você precisa. Agora, porém, você deve perceber que um é um valor controlado pelo cliente que pode, portanto, não ser confiável para uso na lógica de negócios e o outro é um valor controlado pelo servidor que é mais confiável. No entanto, você precisa garantir que o servidor da web em questão esteja SERVER_NAMEconfigurado corretamente. Tomando o Apache HTTPD como exemplo, aqui está um extrato de sua documentação :

Se não ServerNamefor especificado, o servidor tentará deduzir o nome do host executando uma pesquisa inversa no endereço IP. Se nenhuma porta for especificada no ServerName, o servidor usará a porta da solicitação recebida. Para confiabilidade e previsibilidade ideais, você deve especificar um nome de host e porta explícitos usando a ServerNamediretiva.


Atualização : depois de verificar a resposta de Pekka na sua pergunta, que contém um link para a resposta de bobince, pela qual o PHP sempre retornaria HTTP_HOSTo valor SERVER_NAME, o que contraria minhas próprias experiências com o PHP 4.x + Apache HTTPD 1.2.x de alguns anos atrás , Soprei um pouco de poeira do meu ambiente atual do XAMPP no Windows XP (Apache HTTPD 2.2.1 com PHP 5.2.8), iniciei-o, criei uma página PHP que imprime os dois valores, criei um aplicativo de teste Java usando URLConnectionpara modificar oHost cabeçalho e testes me ensinaram que esse é realmente (incorretamente) o caso.

Depois de suspeitar do PHP e pesquisar alguns relatórios de erros do PHP sobre o assunto, aprendi que a raiz do problema está no servidor da web usado, que retornou incorretamente o Hostcabeçalho HTTP quando SERVER_NAMEsolicitado. Então, procurei nos relatórios de erros do Apache HTTPD usando várias palavras-chave relacionadas ao assunto e finalmente encontrei um bug relacionado . Esse comportamento foi introduzido desde o Apache HTTPD 1.3. Você precisa definir UseCanonicalNamedirectiva onna <VirtualHost>entrada do ServerNamena httpd.conf(verifique também o aviso na parte inferior do documento !).

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

Isso funcionou para mim.

Resumido, SERVER_NAMEé mais confiável, mas você depende da configuração do servidor!

BalusC
fonte
5
Ok, isso resolve meu problema, que não está relacionado ao OP, mas é relevante. Eu estava muito preocupado com problemas de segurança usando qualquer coisa que um navegador pudesse fornecer. Esta resposta foi uma enorme ajuda. Obrigado por reservar um tempo para montá-lo.
Yitzhak
2
Por que você diz que HTTP_HOST não é confiável? Sim, é fornecido pelo usuário, mas se o usuário der algum valor falso, a configuração do servidor retornará automaticamente 503 e o script PHP nem será executado!
Pacerier
1
@ Pacerier: no momento em que escrevemos esta resposta, isso não aconteceu. As versões são mencionadas na resposta. Eu não acompanho mais o PHP, então não posso dizer se ele realmente mudou em uma versão mais recente.
BalusC
2
Uma maneira fácil de enganar o Apache do WinXP é adicionar uma linha ao arquivo 'hosts', informando que o IP do servidor está atribuído a outro domínio, como este: "127.0.0.1 mydomain.com". Eu usei isso várias vezes para mostrar um site local enganando meu público e acho que eu tenho conexão com a Internet e site carregado muito rápido. Você pode seguir o caminho inverso e enganar o Apache para que ele esteja executando localmente, com "173.194.41.5 localhost", para que você nunca deva confiar totalmente no SERVER_NAME, a menos que tenha certeza de que o Apache está bem configurado.
Vicenteherrera # 8/13
1
Eu só quero acrescentar que o NGINX + PHP-FPM retorna o valor definido pela server_namediretiva. Especialmente se não server_nameestiver definido, também _SERVER["SERVER_NAME"]estará vazio.
white_gecko
69

HTTP_HOSTé o host de destino enviado pelo cliente. Pode ser manipulado livremente pelo usuário. Não há problema em enviar uma solicitação ao seu site solicitando um HTTP_HOSTvalor de www.stackoverflow.com.

SERVER_NAMEvem da VirtualHostdefinição do servidor e, portanto, é considerado mais confiável. No entanto, ele também pode ser manipulado externamente sob certas condições relacionadas à configuração do servidor da Web: Consulte esta pergunta SO que trata dos aspectos de segurança de ambas as variações.

Você não deve confiar em nenhum deles para estar seguro. Dito isto, o que usar realmente depende do que você deseja fazer. Se você quiser determinar em qual domínio seu script está sendo executado, poderá usá-lo com segurança HTTP_HOST, desde que valores inválidos provenientes de um usuário mal-intencionado não possam quebrar nada.

Pekka
fonte
8
Sim, mas uma solicitação solicitando um valor HTTP_HOST de www.stackoverflow.com seria rejeitada pela maioria dos servidores HTTP na frente, para que o script PHP nem visse a solicitação!
Pacerier
2
@Pacerier true, mas nem sempre se o servidor não estiver configurado corretamente.
Pekka
1
Conforme mencionado na publicação do BalusC, quando você acessa um host virtual do Apache por IP, essas duas variáveis ​​contêm o IP (por padrão), não o nome real do servidor. Você precisa usar UseCanonicalName onno httpd.conf para forçar SERVER_NAMEa ser o nome do servidor real.
Simon Médio
@Pekka 웃, se o servidor não estiver configurado corretamente, $_SERVER['SERVER_NAME']também não funcionaria . Um servidor mal configurado será definido com $_SERVER['SERVER_NAME']base no valor da Host:solicitação do cliente . Ambos são iguais.
Pacerier 5/03
Boa resposta, mas eu não assumiria hospedagem virtual.
Anthony Rutledge
55

Como mencionei nesta resposta , se o servidor for executado em uma porta diferente de 80 (como pode ser comum em uma máquina de desenvolvimento / intranet), ela HTTP_HOSTconterá a porta, enquanto SERVER_NAMEnão.

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

(Pelo menos foi o que notei nos virtualhosts baseados em porta do Apache)

Observe que HTTP_HOSTele não contém :443ao executar em HTTPS (a menos que você esteja executando em uma porta não padrão, que eu não testei).

Como outros observaram, os dois também diferem ao usar o IPv6:

$_SERVER['HTTP_HOST'] == '[::1]'
$_SERVER['SERVER_NAME'] == '::1'
Simon East
fonte
2
Quando eles resolverão esse comportamento insidioso?
Pacerier
27

Observe que, se você deseja usar o IPv6, provavelmente deseja usá-lo em HTTP_HOSTvez de SERVER_NAME. Se você inserir http://[::1]/as variáveis ​​de ambiente, será o seguinte:

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

Isso significa que, se você fizer um mod_rewrite, por exemplo, poderá obter um resultado desagradável. Exemplo para um redirecionamento 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}/

Isso se aplica SOMENTE se você acessar o servidor sem um nome de host.

Daniel Marschall
fonte
1
SiteGround, em seu inhouse http para https código de redirecionamento, o usohttps://%{SERVER_NAME}%{REQUEST_URI}
IXN
6

Se você deseja verificar através de um server.php ou o que quer, você pode chamá-lo com o seguinte:

<?php
    phpinfo(INFO_VARIABLES);
?>

ou

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

    print_r($_SERVER);
?>

Em seguida, acesse-o com todos os URLs válidos para o seu site e confira a diferença.

stevewh
fonte
5

Depende do que eu quero descobrir. SERVER_NAME é o nome do host do servidor, enquanto HTTP_HOST é o host virtual ao qual o cliente se conectou.

Rowland Shaw
fonte
4
Rowland não é exatamente verdadeiro, SERVER_NAMEgeralmente é o nome do VirtualHost, não o próprio servidor. E no Apache, SERVER_NAMEgeralmente é preenchido com o mesmo valor que HTTP_HOST(consulte a resposta do BalusC).
Simon Médio
1
@ Simon, Como os hosts mosts agora são VirtualHost, o que você quer dizer com o nome de "o próprio servidor"?
Pacerier
Se você estiver executando um servidor virtual privado (VPS) com um site, não será necessário assumir que isso SERVER_NAMEse aplica a um host virtual. No entanto, ainda é possível usar uma configuração de host virtual para um site. Muitas pessoas usam hospedagem compartilhada, então eu entendo o seu ponto.
Anthony Rutledge
2

Demorei um pouco para entender o que as pessoas queriam dizer com ' SERVER_NAMEé mais confiável'. Eu uso um servidor compartilhado e não tem acesso a diretivas de host virtual. Então, eu uso mod_rewrite .htaccess para mapear HTTP_HOSTs diferentes para diretórios diferentes. Nesse caso, éHTTP_HOST que é significativo.

A situação é semelhante se alguém usar hosts virtuais baseados em nome: a ServerNamediretiva em um host virtual simplesmente diz qual nome do host será mapeado para esse host virtual. A conclusão é que, em ambos os casos, o nome do host fornecido pelo cliente durante a request ( HTTP_HOST) deve corresponder a um nome no servidor, que é mapeado para um diretório. Se o mapeamento é feito com diretivas de host virtual ou com regras htaccess mod_rewrite, é secundário aqui. Nestes casos, HTTP_HOSTserá o mesmo queSERVER_NAME . Fico feliz que o Apache esteja configurado dessa maneira.

No entanto, a situação é diferente com hosts virtuais baseados em IP. Nesse caso, e somente neste caso, SERVER_NAMEe HTTP_HOSTpode ser diferente, porque agora o cliente seleciona o servidor pelo IP, não pelo nome. De fato, pode haver configurações especiais onde isso é importante.

Então, a partir de agora, usarei SERVER_NAME, caso meu código seja portado nessas configurações especiais.

Dominic108
fonte
2

Supondo que um tenha uma configuração simples (CentOS 7, Apache 2.4.xe PHP 5.6.20) e apenas um site (não assumindo hospedagem virtual) ...

No sentido PHP, $_SERVER['SERVER_NAME']é um elemento que o PHP registra no $_SERVERsuperglobal com base na sua configuração do Apache ( **ServerName**diretiva com UseCanonicalName On) no httpd.conf (seja de um arquivo de configuração do host virtual incluído, o que seja, etc ...). HTTP_HOST é derivado do HTTPhost cabeçalho . Trate isso como entrada do usuário. Filtre e valide antes de usar.

Aqui está um exemplo de onde eu uso $_SERVER['SERVER_NAME']como base para uma comparação. O método a seguir é de uma classe filho concreta que eu criei chamada ServerValidator(filho de Validator). ServerValidatorverifica seis ou sete elementos em $ _SERVER antes de usá-los.

Para determinar se a solicitação HTTP é POST, eu uso esse método.

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

Quando esse método é chamado, toda a filtragem e validação dos elementos $ _SERVER relevantes já teriam ocorrido (e as propriedades relevantes foram definidas).

A linha ...

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

... verifica se o $_SERVER['HTTP_HOST']valor (finalmente derivado do hostcabeçalho HTTP solicitado ) corresponde $_SERVER['SERVER_NAME'].

Agora, eu estou usando falar superglobal para explicar o meu exemplo, mas isso é apenas porque algumas pessoas não estão familiarizados com INPUT_GET, INPUT_POSTe INPUT_SERVERno que diz respeito a filter_input_array().

A conclusão é que não lida com solicitações POST no meu servidor, a menos que todas as quatro condições sejam atendidas. Por isso, em termos de solicitações POST, a incapacidade de fornecer um HTTP hostcabeçalho (presença testado para anteriores) períodos DooM para rigorosas HTTP 1.0 navegadores. Além disso, o anfitrião solicitado deve corresponder ao valor de ServerNameno httpd.conf , e, por extensão, o valor para $_SERVER('SERVER_NAME')no $_SERVERsuperglobal. Mais uma vez, eu usaria INPUT_SERVERcom as funções de filtro PHP, mas você entendeu a minha deriva.

Lembre-se de que o Apache costuma usar ServerNameem redirecionamentos padrão (como deixar a barra à direita de uma URL: exemplo, http://www.foo.com se torna http://www.foo.com/ ), mesmo que você não esteja usando a reescrita de URL.

Eu uso $_SERVER['SERVER_NAME']como padrão, não $_SERVER['HTTP_HOST']. Há muitas discussões sobre esse assunto. $_SERVER['HTTP_HOST']pode estar vazio, portanto, essa não deve ser a base para a criação de convenções de código, como meu método público acima. Mas, apenas porque ambos podem ser definidos, não garante que serão iguais. Testar é a melhor maneira de ter certeza (tendo em mente a versão Apache e a versão PHP).

Anthony Rutledge
fonte
0

Como o balusC disse que SERVER_NAME não é confiável e pode ser alterado na configuração do apache, na configuração do nome do servidor e no firewall que pode estar entre você e o servidor.

A função a seguir sempre retorna o host real (host digitado pelo usuário) sem porta e é quase confiável:

function getRealHost(){
   list($realHost,)=explode(':',$_SERVER['HTTP_HOST']);
   return $realHost;
}
MSS
fonte
0

$ _SERVER ['SERVER_NAME'] se baseia na configuração dos seus servidores web. $ _SERVER ['HTTP_HOST'] é baseado na solicitação do cliente.

Vitalie
fonte