# display_errors off: desativar a saída de erros em produção

> display_errors Off e mais: que definições do PHP mantêm as mensagens de erro fora do site em produção, o que as frameworks acrescentam e como detetar fugas.

Fonte: https://phpsos.de/pt/blog/display-errors-produktion/  
Atualizado em: 25/10/2026  
Prestador: PHP SOS, Assistência de emergência PHP e segurança de servidores. Ajuda imediata por WhatsApp: https://wa.me/491736494914?text=Ol%C3%A1%2C%20PHP%20SOS%2C%20precisamos%20de%20ajuda%20com%20o%20nosso%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**Em resumo:** Num site em produção, `display_errors = Off` pertence ao php.ini, juntamente com `display_startup_errors = Off`, `log_errors = On` e um registo de erros fora da raiz web. Além disso, os CMS e as frameworks têm de correr em modo de produção; caso contrário, as suas páginas de erro próprias mostram os pormenores. Bloqueados com `php_admin_flag` e verificados após cada atualização, os erros ficam no registo em vez de no navegador.

## O que uma única mensagem de erro revela

Um aviso no topo da página, um «Fatal error» em vez do resumo da encomenda: estas saídas parecem inofensivas. Para um atacante, são um mapa. Mostram caminhos completos de ficheiros, nomes de bases de dados e tabelas, bibliotecas utilizadas e, muitas vezes, a versão do PHP.

Precisamente em situações como o PHP2Shell, em que se procuram servidores vulneráveis em massa, um servidor deve revelar o mínimo possível sobre si. Isso não fecha nenhuma vulnerabilidade no interpretador PHP, mas dificulta cada passo seguinte a uma intrusão e torna os ataques dirigidos à sua aplicação mais trabalhosos.

Acresce a proteção de dados. As mensagens de erro podem conter partes de pedidos, por exemplo endereços de e-mail de formulários. O que aparece no navegador de um visitante deixa de estar sob o seu controlo.

## Os valores por defeito não foram pensados para a produção

Muitos partem do princípio de que o PHP oculta os erros por si. O manual do PHP diz outra coisa: o valor por defeito integrado do `display_errors` é 1, e o `display_startup_errors` está também ativado por defeito desde o PHP 8.0. Só o modelo fornecido `php.ini-production` define ambos como Off. O manual descreve expressamente o `display_errors` como uma funcionalidade de desenvolvimento que nunca deve ser usada em sistemas na Internet.

Se o seu servidor usa o modelo de produção não é evidente. As imagens de contentor arrancam por vezes sem qualquer php.ini, os painéis de alojamento definem valores próprios e, após uma mudança, copia-se facilmente o modelo de desenvolvimento.

## display_errors off: as definições em resumo

Os valores seguintes fazem sentido para servidores de produção. As constantes como `E_ALL` só funcionam no php.ini; na configuração do Apache não têm, segundo o manual do PHP, qualquer significado e têm de ser indicadas como número.

| Definição | Produção | Finalidade |
|---|---|---|
| `display_errors` | `Off` | nenhum erro no navegador |
| `display_startup_errors` | `Off` | nenhum erro do arranque do PHP na saída |
| `log_errors` | `On` | escrever os erros no registo |
| `error_log` | caminho fora da raiz web | ficheiro de registo próprio por site ou pool |
| `error_reporting` | `E_ALL` | registar todos os erros, não os mostrar |
| `html_errors` | `Off` | texto simples em vez de HTML nos registos |
| `zend.exception_ignore_args` | `On` | sem argumentos de funções nos stack traces |
| `fatal_error_backtraces` | `0` até à atualização (apenas PHP 8.5) | medida transitória segundo o php.net |

O `zend.exception_ignore_args` é particularmente valioso. Sem esta definição, os stack traces em registos e páginas de erro podem conter os argumentos das chamadas de funções e, portanto, também palavras-passe ou tokens que estavam a ser processados.

```ini
; php.ini da SAPI web
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php/website-fehler.log
error_reporting = E_ALL
html_errors = Off
zend.exception_ignore_args = On
; apenas PHP 8.5, medida transitória até à atualização para 8.5.12:
; fatal_error_backtraces = 0
```

No pool FPM, bloqueie adicionalmente os valores mais importantes, para que nenhuma aplicação os volte a ativar com `ini_set()`:

```ini
; p. ex. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_flag[display_errors] = off
php_admin_flag[display_startup_errors] = off
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php/seite1-fehler.log
```

Depois, reinicie o respetivo serviço PHP. Como funciona exatamente o bloqueio é explicado no artigo [php_admin_value: bloquear definições contra a sobrescrita](https://phpsos.de/pt/blog/php-admin-value-sperren/). Encontra uma configuração de base completa em [Configurar o php.ini com segurança](https://phpsos.de/pt/ratgeber/php-ini-sicher-konfigurieren/).

## O segundo interruptor: o modo de depuração da aplicação

O PHP é só metade do caminho. As frameworks modernas intercetam elas próprias os erros e mostram páginas de erro próprias, muitas vezes muito detalhadas. No modo de depuração, contêm excertos do código-fonte, valores de configuração e, por vezes, variáveis de ambiente. Estas páginas aparecem mesmo que o `display_errors` esteja desativado no PHP.

Verifique por isso, para cada aplicação, o interruptor próprio. No WordPress, são o `WP_DEBUG` e o `WP_DEBUG_DISPLAY` no `wp-config.php`; no Laravel e no Symfony, o `APP_DEBUG` e o ambiente no ficheiro `.env`; noutros sistemas, o modo de produção conforme a documentação do fabricante. Num sistema em produção, o modo de depuração tem de estar permanentemente desativado; mesmo «só por um instante para testar» é um risco.

Como se tratam corretamente os erros na aplicação, em vez de os suprimir, é descrito em [Erros e exceções em PHP](https://phpsos.de/pt/php/fehler-und-exceptions/).

## Como encontrar saídas de erro antes de outros o fazerem

Definir uma vez não basta. As saídas de erro regressam após atualizações, mudanças ou alterações no painel. Um breve procedimento de verificação ajuda:

1. Verificar os valores efetivos no contexto web, não apenas na linha de comandos, por exemplo com um ficheiro de teste colocado por breves instantes e eliminado logo a seguir.
2. Controlar o modo de depuração de cada aplicação no respetivo ficheiro de configuração.
3. Pesquisar nas páginas entregues textos de erro típicos, como no bloco de código abaixo.
4. No motor de busca, procurar com uma consulta `site:` sobre o seu domínio termos como «Fatal error» ou «Warning».
5. Verificar se o registo de erros está a ser escrito e não se encontra na raiz web.

```bash
# Pesquisar nas páginas entregues textos de erro do PHP (apenas domínios próprios)
for url in https://www.example.de/ https://www.example.de/kontakt/; do
  curl -s "$url" | grep -E -o "(Fatal error|Warning|Notice|Deprecated|Stack trace)[^<]{0,80}" | head -3
done
# O registo de erros está a ser escrito?
ls -l /var/log/php/
```

> **Importante:**
> Esconder erros não substitui corrigi-los. Um registo cheio de avisos é um indício de necessidade de manutenção e, após uma intrusão, é muitas vezes um vestígio importante. O que deve ou não registar é descrito em [Registar com segurança: registar erros sem fuga de dados](https://phpsos.de/pt/blog/sicher-loggen-php/).

## Conclusão: silencioso no navegador, falador no registo

A regra para servidores de produção é simples: os erros pertencem ao registo, não ao site. Na prática, falha por causa de valores por defeito que não foram pensados para a produção, de interruptores de depuração em aplicações e de configurações que regridem após atualizações ou mudanças.

Quem desativa `display_errors` e `display_startup_errors`, bloqueia os valores no pool, controla o modo de depuração da aplicação e verifica o resultado regularmente fecha uma das fontes mais frequentes de divulgação desnecessária de informação. O esforço é reduzido e o efeito dura, desde que a verificação continue a fazer parte de cada atualização.

Um ponto não deve passar despercebido: o registo de erros não é um caixote do lixo. Quem o lê regularmente reconhece atualizações defeituosas, funções obsoletas e, por vezes, os primeiros vestígios de um ataque, muito antes de os clientes notarem alguma coisa.

## Como ajuda concretamente a PHP SOS?

- **Verificar:** lemos as definições de erro efetivas por site e por pool e pesquisamos nas páginas entregues saídas de erro.
- **Alterar:** definimos as configurações do PHP e da aplicação para a operação em produção e bloqueamo-las contra sobrescrita.
- **Configurar registos:** os registos de erros ficam fora da raiz web, são rodados e estão acessíveis aos seus programadores.
- **Manter de forma duradoura:** no [contrato de manutenção de segurança](https://phpsos.de/pt/leistungen/security-wartungsvertrag/), voltamos a verificar as definições após cada atualização.


## Perguntas frequentes

**O display_errors não está já desativado no PHP?**  
Não automaticamente. Segundo o manual do PHP, o valor por defeito integrado do display_errors é 1. Só o modelo fornecido php.ini-production define Off. Se o seu servidor usa este modelo depende da distribuição, do painel ou da imagem de contentor e deve ser verificado.

**O display_errors = Off basta se o WordPress ou o Laravel estiverem em modo de depuração?**  
Não. Muitos CMS e frameworks trazem páginas de erro próprias, que podem mostrar pormenores independentemente do PHP. Desative por isso também o modo de depuração da aplicação, por exemplo WP_DEBUG_DISPLAY no WordPress ou APP_DEBUG no Laravel e no Symfony.

**Para onde vão os erros quando deixam de ser mostrados?**  
Com log_errors = On, o PHP escreve-os no ficheiro indicado por error_log ou no registo do respetivo modo de operação, por exemplo o registo de erros do Apache ou do PHP-FPM. O ficheiro deve estar fora da raiz web e ser rodado regularmente.

**O que tem o fatal_error_backtraces a ver com o PHP2Shell?**  
A definição existe desde o PHP 8.5 e está ativa por defeito. O php.net indica fatal_error_backtraces=0, com reinício posterior, como uma das medidas transitórias para o PHP 8.5, enquanto a atualização para 8.5.12 não for feita. Não substitui a atualização.

## Fontes

- [Manual do PHP: tratamento de erros e registo](https://www.php.net/manual/en/errorfunc.configuration.php)
- [Manual do PHP: alterar a configuração (php_admin_flag)](https://www.php.net/manual/en/configuration.changes.php)
- [php.net: comunicados de segurança](https://www.php.net/)
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
