Abra a aba de rede do seu navegador em qualquer site baseado em PHP e veja os cabeçalhos de resposta para o documento HTML principal. Em muitas instalações, você encontrará um cabeçalho que diz algo como X-Powered-By: PHP/8.1.27. Esse é o PHP gentilmente dizendo a toda a internet qual versão está executando. É um pequeno detalhe que a maioria dos proprietários de sites nunca percebe, mas é uma das informações mais fáceis que um invasor coleta antes de decidir se vale a pena investigar mais um site.
Este guia explica por que o cabeçalho existe em primeiro lugar, o que ele realmente expõe e todas as maneiras realistas de se livrar dele. Da configuração do servidor até o nível de um único site WordPress em hospedagem compartilhada onde você não pode tocar no php.ini.
O que o cabeçalho realmente revela a um invasor
Um valor como PHP/8.1.27 revela dois fatos úteis. Primeiro, a versão principal. Se você ainda está no PHP 7.4 ou PHP 8.0, ambos com fim de vida útil, um invasor sabe imediatamente que nenhum patch de segurança virá. Segundo, o nível exato do patch. Entre quaisquer duas versões de patch, geralmente há um punhado de CVEs publicamente conhecidos. Um scanner pode comparar sua versão relatada com seu banco de dados de CVE e decidir em milissegundos se continua sondando ou segue em frente.
Ocultar a versão obviamente não corrige nada. Se seu PHP estiver desatualizado, ainda estará desatualizado. Mas remover o anúncio faz três coisas concretas: impede que scanners automatizados em massa marquem seu site como um alvo barato, torna o reconhecimento manual ligeiramente mais caro e remove uma informação que simplesmente não tem por que estar em seus cabeçalhos de resposta. Não há razão legítima para a internet pública saber o nível do seu patch do PHP.
Algumas estruturas de conformidade e listas de verificação de segurança (PCI-DSS, BSI IT-Grundschutz, auditorias internas em empresas maiores) explicitamente pedem que esse cabeçalho seja suprimido. Mesmo se você não trabalha em um desses contextos, é uma correção de cinco minutos com zero desvantagem.
De onde vem o cabeçalho?
O cabeçalho X-Powered-By é emitido pelo próprio PHP, não pelo seu servidor web. O comportamento é controlado por uma única diretiva do php.ini chamada expose_php. Quando essa diretiva está definida como On (que é o padrão na maioria das distribuições do PHP), o PHP adiciona o cabeçalho a cada resposta automaticamente. Desligue-a e o cabeçalho desaparece para cada site naquela instalação do PHP.
Além disso, alguns frameworks ou aplicações adicionam seus próprios valores X-Powered-By. Express, ASP.NET e outros fazem isso. O WordPress em si não, mas plugins ocasionalmente fazem. Então, depois de desligar a divulgação de versão do PHP, vale a pena verificar os cabeçalhos de resposta mais uma vez para ver se algo mais ainda está vazando.
Opção 1: Desabilitar expose_php no php.ini
Esta é a correção mais limpa se você controla o servidor. Abra seu arquivo php.ini. No Linux, o caminho geralmente é um destes:
/etc/php/8.3/fpm/php.inipara PHP-FPM, a configuração mais comum por trás do nginx e do Apache moderno/etc/php/8.3/apache2/php.inipara Apache com mod_php/etc/php/8.3/cli/php.inipara a linha de comando, o que normalmente não importa neste contexto
O número da versão no caminho corresponderá à versão do PHP em que você está. Encontre a linha que diz:
expose_php = OnMude para:
expose_php = OffSalve o arquivo e reinicie o processo PHP para que a alteração tenha efeito. Para PHP-FPM, isso fica assim:
sudo systemctl restart php8.3-fpmPara Apache com mod_php:
sudo systemctl restart apache2Para verificar, execute um curl rápido contra seu site e olhe os cabeçalhos:
curl -I https://seudominio.comA linha X-Powered-By deve ter desaparecido completamente. Se ainda estiver lá, você editou o arquivo php.ini errado (frequentemente há mais de um). Verifique qual está realmente carregado com php --ini na linha de comando, ou colocando uma chamada temporária phpinfo() no site (e removendo-a imediatamente depois, por favor).
Opção 2: Definir expose_php por site via .user.ini
Nem todos têm acesso ao php.ini global. Em muitos hosts compartilhados você ainda pode influenciar as configurações do PHP através de um arquivo chamado .user.ini, que você coloca na raiz do documento do seu site. Crie (ou edite) um .user.ini com:
expose_php = OffO PHP verifica esse arquivo a cada requisição, mas armazena em cache o resultado por alguns minutos por padrão (controlado por user_ini.cache_ttl, geralmente 300 segundos). Então dê cinco minutos, limpe quaisquer caches de borda e verifique seus cabeçalhos novamente.
Uma palavra de cautela: nem todos os hosts permitem que expose_php seja sobrescrito de .user.ini. A diretiva tem o modo PHP_INI_PERDIR, que tecnicamente permite isso, mas alguns hosts explicitamente colocam em whitelist o que pode ser alterado. Se a configuração não tiver efeito após alguns minutos, seu host provavelmente está bloqueando-a. Pule para a Opção 5.
Opção 3: Remover o cabeçalho no Apache
Você também pode remover o cabeçalho no nível do servidor web, o que funciona não importa o que o PHP esteja fazendo internamente. Em sua configuração do Apache (ou no .htaccess do site, se AllowOverride permitir), adicione:
<IfModule mod_headers.c>
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>A razão para usar tanto unset quanto always unset é que a versão regular só se aplica a respostas 2xx bem-sucedidas. A variante always também se aplica a respostas de erro como páginas 500, que é exatamente onde você não quer que a versão vaze também.
O mod_headers precisa estar habilitado para que isso funcione. No Debian e Ubuntu:
sudo a2enmod headers
sudo systemctl reload apache2Se você está em hospedagem compartilhada com Apache, a variante .htaccess quase sempre funciona, porque a maioria dos hosts habilita mod_headers e permite via AllowOverride All. Se a regra não tiver efeito, pergunte ao seu host se mod_headers está disponível.
Opção 4: Remover o cabeçalho no nginx
No nginx, o equivalente vai no bloco server do seu site, ou em um bloco http geral se quiser para todo o site:
more_clear_headers 'X-Powered-By';Pequeno detalhe: more_clear_headers requer o headers-more-nginx-module, que não faz parte do pacote nginx padrão em todas as distribuições. Debian e Ubuntu o entregam no pacote nginx-extras:
sudo apt install nginx-extrasSe você está preso ao nginx puro sem o módulo extras, há uma solução alternativa usando fastcgi_hide_header. Esta é na verdade a ferramenta certa se o cabeçalho está vindo do PHP-FPM (que na maioria dos casos é):
location ~ \.php$ {
# your usual fastcgi_pass and params
fastcgi_hide_header X-Powered-By;
}Recarregue o nginx com sudo nginx -t && sudo systemctl reload nginx e verifique com curl -I.
Opção 5: Caso especial, WordPress
O WordPress em si não define X-Powered-By. O cabeçalho vem do PHP. Então tudo acima se aplica inalterado: desligar expose_php ou remover o cabeçalho no Apache ou nginx também resolverá para um site WordPress. Mas o WordPress lhe dá dois ângulos extras que vale a pena conhecer.
Suprimir o cabeçalho de dentro do WordPress. Se você não tem acesso à configuração do servidor web e .user.ini não funciona em seu host, você pode remover o cabeçalho programaticamente através de um must-use plugin. Crie um arquivo em wp-content/mu-plugins/remove-powered-by.php (crie a pasta mu-plugins se ainda não existir) e coloque isto dentro:
<?php
/**
* Plugin Name: Remove X-Powered-By
* Description: Strips the X-Powered-By header from every WordPress response.
*/
if (!defined('ABSPATH')) {
exit;
}
add_action('send_headers', function () {
if (function_exists('header_remove')) {
header_remove('X-Powered-By');
}
});Duas coisas a observar com essa abordagem. Primeiro, header_remove() só funciona se o PHP ainda não começou a enviar o corpo da resposta. O hook send_headers dispara no momento certo para toda página WordPress normal, então isso funciona para posts, páginas, admin e a REST API. Não ajuda para requisições que nunca chegam ao WordPress, como chamadas diretas a arquivos estáticos ou páginas em cache servidas por um proxy reverso. Segundo, alguns hosts rodam um proxy reverso (Cloudflare, Varnish, Nginx na frente do Apache) que lê o cabeçalho da resposta do backend e o encaminha como está. Se o cabeçalho ainda aparece em seu navegador após instalar este plugin, o proxy está cacheando-o. Limpe o cache e, idealmente, remova o cabeçalho no nível do proxy também.
O WordPress também expõe sua própria versão. Já que está nisso, você provavelmente quer lidar com a tag <meta name="generator" content="WordPress X.Y.Z"> que o WordPress coloca no head do HTML, e os parâmetros ?ver=X.Y.Z nos arquivos CSS e JS. Estes não são iguais ao cabeçalho de versão do PHP, mas vazam a versão exata do WordPress, que é comparativamente útil para um invasor. O trecho abaixo lida com ambos:
<?php
// Remove the generator meta tag
remove_action('wp_head', 'wp_generator');
// Strip the WordPress version from RSS feeds, admin, etc.
add_filter('the_generator', '__return_empty_string');
// Remove ?ver=X.Y.Z from CSS and JS file URLs
add_filter('style_loader_src', function ($src) {
return remove_query_arg('ver', $src);
}, 9999);
add_filter('script_loader_src', function ($src) {
return remove_query_arg('ver', $src);
}, 9999);Note que remover o parâmetro ?ver= tem um efeito colateral: o navegador não consegue mais saber quando um arquivo CSS ou JS mudou apenas pela URL. Se você depende disso para cache busting, ou deixe o filtro de fora ou substitua ver por um hash do conteúdo do arquivo.
Opção 6: Cloudflare, Sucuri e outros proxies reversos
Se você está atrás do Cloudflare ou de um CDN semelhante, pode remover o cabeçalho mais uma camada acima, o que é bom porque cobre todos os sites que você roteia pela mesma zona sem tocar na origem. No Cloudflare isso é feito via uma Transform Rule do tipo "Modify Response Header":
- Nome da regra: "Remove X-Powered-By"
- Quando requisições recebidas correspondem:
Hostname equals seudominio.com(ou um curinga se você quiser para toda a zona) - Então: Remover cabeçalho
X-Powered-By
Sucuri, BunnyCDN, Fastly e a maioria dos outros proxies reversos oferecem um equivalente. A redação exata no painel varia, mas o recurso que você está procurando é sempre "modificar cabeçalhos de resposta" ou "regras de borda".
Outros cabeçalhos que vale a pena verificar enquanto está nisso
O PHP não é a única coisa que tende a falar muito sobre si nos cabeçalhos de resposta. Enquanto tem o curl aberto, varra os cabeçalhos em busca de qualquer um dos seguintes:
Server: Apache/2.4.58 (Ubuntu)revela a versão do patch do Apache e o sabor do seu SO. Suprima viaServerTokens ProdeServerSignature Offno Apache, ouserver_tokens offno nginx.X-Powered-CMS,X-Generator,X-Drupal-Cacheou similar. Geralmente definidos por plugins ou módulos específicos do CMS. Remova da mesma maneira queX-Powered-By.X-AspNet-VersionouX-AspNetMvc-Version. Relevante apenas se algo em sua stack toca .NET, mas vale a pena saber.
O InspectWP sinaliza todos esses na seção de cabeçalhos de segurança do seu relatório. Uma vez que tenha a versão do PHP oculta, executar uma nova varredura é a maneira mais rápida de ver quais outras pequenas pistas ainda estão na conexão.
Verifique e está pronto
Após qualquer rota que você escolheu, faça uma última verificação:
- Abra um terminal e execute
curl -I https://seudominio.com. - Olhe a saída. Não deve haver nenhuma linha
X-Powered-By. - Repita para uma URL admin (para WordPress, tente
wp-login.php) e para uma página de erro (acrescente uma string aleatória à URL para disparar um 404). O cabeçalho deve permanecer ausente em todos os lugares. - Se você usa Cloudflare ou um cache, teste também a partir de um IP novo ou com cache bypass, para que saiba que está olhando uma resposta ao vivo e não uma cacheada.
- Execute uma nova varredura do InspectWP. A verificação de versão do PHP na seção Hospedagem agora cairá em outros métodos de detecção ou marcará a versão como não divulgada, que é o que você quer.
Ocultar a versão do PHP não é um substituto para manter o PHP atualizado. É um pequeno passo em um quadro maior de fortalecimento. Mas é o tipo de vitória rápida que leva cinco minutos, remove uma informação gratuita que invasores caso contrário coletam de graça e custa exatamente zero em troca.