# display_errors off: desactivar la salida de errores en producción

> display_errors Off y más: qué ajustes de PHP mantienen los mensajes de error fuera del sitio web en producción, qué aportan los frameworks y cómo detectar fugas.

Fuente: https://phpsos.de/es/blog/display-errors-produktion/  
Actualizado: 25/10/2026  
Proveedor: PHP SOS, Ayuda de emergencia para PHP y seguridad de servidores. Ayuda inmediata por WhatsApp: https://wa.me/491736494914?text=Hola%20PHP%20SOS%2C%20necesitamos%20ayuda%20con%20nuestro%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Sitio%20web%3A%20%E2%80%A6

**En resumen:** en un sitio web en producción, `display_errors = Off` pertenece al php.ini, junto con `display_startup_errors = Off`, `log_errors = On` y un log de errores fuera del webroot. Además, los CMS y frameworks deben funcionar en modo de producción; de lo contrario, sus propias páginas de error muestran los detalles. Con el valor bloqueado mediante `php_admin_flag` y comprobado tras cada actualización, los errores quedan en el log y no en el navegador.

## Lo que revela un solo mensaje de error

Una advertencia en el borde superior de la página, un «Fatal error» en lugar del resumen del pedido: estas salidas parecen inofensivas. Para un atacante son un mapa. Muestran rutas completas de archivos, nombres de bases de datos y tablas, bibliotecas en uso y, a menudo, la versión de PHP.

Precisamente en situaciones como PHP2Shell, en las que se busca de forma masiva servidores vulnerables, un servidor debería revelar lo menos posible sobre sí mismo. Eso no cierra ninguna brecha en el intérprete de PHP, pero dificulta cada paso posterior a una intrusión y hace más laboriosos los ataques dirigidos contra su aplicación.

A ello se suma la protección de datos. Los mensajes de error pueden contener partes de peticiones, por ejemplo direcciones de correo de formularios. Lo que aparece en el navegador de un visitante ya no está bajo su control.

## Los valores por defecto no están pensados para producción

Muchos suponen que PHP oculta los errores por sí mismo. El manual de PHP dice otra cosa: el valor por defecto integrado de `display_errors` es 1, y `display_startup_errors` también está activado por defecto desde PHP 8.0. Solo la plantilla incluida `php.ini-production` pone ambos en Off. El manual califica expresamente `display_errors` de función de desarrollo que nunca debería usarse en sistemas conectados a internet.

Que su servidor use la plantilla de producción no es algo evidente. Las imágenes de contenedor a veces arrancan sin php.ini en absoluto, los paneles de hosting fijan valores propios y, tras una mudanza, se copia con facilidad la plantilla de desarrollo.

## display_errors off: los ajustes de un vistazo

Los valores siguientes son adecuados para servidores de producción. Las constantes como `E_ALL` solo funcionan en el php.ini; en la configuración de Apache carecen, según el manual de PHP, de significado y deben indicarse como número.

| Ajuste | Producción | Finalidad |
|---|---|---|
| `display_errors` | `Off` | ningún error en el navegador |
| `display_startup_errors` | `Off` | ningún error al arrancar PHP en la salida |
| `log_errors` | `On` | escribir los errores en el registro |
| `error_log` | ruta fuera del webroot | archivo de log propio por sitio web o pool |
| `error_reporting` | `E_ALL` | registrar todos los errores, no mostrarlos |
| `html_errors` | `Off` | texto plano en lugar de HTML en los registros |
| `zend.exception_ignore_args` | `On` | sin argumentos de funciones en las trazas de pila |
| `fatal_error_backtraces` | `0` hasta la actualización (solo PHP 8.5) | medida transitoria según php.net |

`zend.exception_ignore_args` es especialmente valioso. Sin este ajuste, las trazas de pila en logs y páginas de error pueden contener los argumentos de las llamadas a funciones, y por tanto también contraseñas o tokens que se estaban procesando.

```ini
; php.ini de la 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
; solo PHP 8.5, medida transitoria hasta la actualización a 8.5.12:
; fatal_error_backtraces = 0
```

En el pool de FPM bloquee además los valores más importantes, para que ninguna aplicación los vuelva a activar mediante `ini_set()`:

```ini
; p. ej. /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
```

Después, reinicie el servicio de PHP correspondiente. Cómo funciona exactamente el bloqueo lo explica el artículo [php_admin_value: bloquear ajustes frente a la sobrescritura](https://phpsos.de/es/blog/php-admin-value-sperren/). Encontrará una configuración básica completa en [Configurar el php.ini de forma segura](https://phpsos.de/es/ratgeber/php-ini-sicher-konfigurieren/).

## El segundo interruptor: el modo de depuración de la aplicación

PHP es solo la mitad del trabajo. Los frameworks modernos capturan los errores por sí mismos y muestran páginas de error propias, a menudo muy detalladas. En modo de depuración contienen extractos del código fuente, valores de configuración y, a veces, variables de entorno. Estas páginas aparecen incluso cuando `display_errors` está desactivado en PHP.

Por eso revise para cada aplicación su propio interruptor. En WordPress son `WP_DEBUG` y `WP_DEBUG_DISPLAY` en `wp-config.php`; en Laravel y Symfony, `APP_DEBUG` y el entorno en el archivo `.env`; en otros sistemas, el modo de producción según la documentación del fabricante. En un sistema en producción, el modo de depuración debe estar desactivado de forma permanente; incluso «solo un momento para probar» es un riesgo.

Cómo se tratan limpiamente los errores en la aplicación, en lugar de suprimirlos, lo describe [Errores y excepciones en PHP](https://phpsos.de/es/php/fehler-und-exceptions/).

## Cómo encontrar salidas de error antes de que lo hagan otros

Configurarlo una vez no basta. Las salidas de error vuelven tras actualizaciones, mudanzas o cambios en el panel. Un breve procedimiento de comprobación ayuda:

1. Comprobar los valores efectivos en el contexto web, no solo en la línea de comandos, por ejemplo con un archivo de prueba colocado un momento y borrado de inmediato.
2. Controlar el modo de depuración de cada aplicación en su archivo de configuración.
3. Recorrer las páginas servidas en busca de textos de error típicos, como en el bloque de código de abajo.
4. Buscar en el buscador, con una consulta `site:` sobre su dominio, términos como «Fatal error» o «Warning».
5. Comprobar que se escribe en el log de errores y que no está en el webroot.

```bash
# Recorrer las páginas servidas en busca de textos de error de PHP (solo dominios propios)
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
# ¿Se escribe en el log de errores?
ls -l /var/log/php/
```

> **Importante:**
> Ocultar errores no sustituye a corregirlos. Un log lleno de advertencias indica necesidad de mantenimiento y, tras una intrusión, suele ser un rastro importante. Qué registrar y qué no lo describe [Registrar con seguridad: anotar errores sin fuga de datos](https://phpsos.de/es/blog/sicher-loggen-php/).

## Conclusión: discreto en el navegador, locuaz en el log

La regla para servidores de producción es sencilla: los errores pertenecen al registro, no al sitio web. En la práctica fracasa por valores por defecto no pensados para producción, por interruptores de depuración en las aplicaciones y por configuraciones que retroceden tras actualizaciones o mudanzas.

Quien desactiva `display_errors` y `display_startup_errors`, bloquea los valores en el pool, controla el modo de depuración de la aplicación y comprueba el resultado con regularidad, cierra una de las fuentes más frecuentes de divulgación innecesaria de información. El esfuerzo es pequeño y el efecto se mantiene mientras la comprobación siga formando parte de cada actualización.

Un punto no debería pasar inadvertido: el log de errores no es una papelera. Quien lo lee con regularidad detecta actualizaciones defectuosas, funciones obsoletas y a veces también los primeros rastros de un ataque, mucho antes de que los clientes noten algo.

## ¿Cómo ayuda PHP SOS en concreto?

- **Comprobar:** leemos los ajustes de error efectivos por sitio web y pool y recorremos las páginas servidas en busca de salidas de error.
- **Cambiar:** fijamos los ajustes de PHP y de la aplicación para el funcionamiento en producción y los bloqueamos frente a la sobrescritura.
- **Configurar los registros:** los logs de errores están fuera del webroot, se rotan y son accesibles para sus desarrolladores.
- **Mantenerlo de forma duradera:** en el [contrato de mantenimiento de seguridad](https://phpsos.de/es/leistungen/security-wartungsvertrag/) volvemos a comprobar los ajustes tras cada actualización.


## Preguntas frecuentes

**¿No está display_errors ya desactivado de todos modos en PHP?**  
No automáticamente. Según el manual de PHP, el valor por defecto integrado de display_errors es 1. Solo la plantilla incluida php.ini-production lo pone en Off. Si su servidor usa esa plantilla depende de la distribución, del panel o de la imagen de contenedor, y conviene comprobarlo.

**¿Basta display_errors = Off si WordPress o Laravel funcionan en modo de depuración?**  
No. Muchos CMS y frameworks traen sus propias páginas de error, que pueden mostrar detalles con independencia de PHP. Por eso desactive también el modo de depuración de la aplicación, por ejemplo WP_DEBUG_DISPLAY en WordPress o APP_DEBUG en Laravel y Symfony.

**¿Adónde van los errores cuando ya no se muestran?**  
Con log_errors = On, PHP los escribe en el archivo que indica error_log, o en el registro del modo de funcionamiento correspondiente, por ejemplo el log de errores de Apache o de PHP-FPM. El archivo debería estar fuera del webroot y rotarse con regularidad.

**¿Qué tiene que ver fatal_error_backtraces con PHP2Shell?**  
El ajuste existe desde PHP 8.5 y está activo por defecto. php.net cita fatal_error_backtraces=0 con el consiguiente reinicio como una de las medidas transitorias para PHP 8.5 mientras esté pendiente la actualización a 8.5.12. No sustituye a la actualización.

## Fuentes

- [Manual de PHP: gestión de errores y registro](https://www.php.net/manual/en/errorfunc.configuration.php)
- [Manual de PHP: modificar la configuración (php_admin_flag)](https://www.php.net/manual/en/configuration.changes.php)
- [php.net: avisos de seguridad](https://www.php.net/)
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
