IT-Admin.tech

Código de error 500 en Apache tras el reinicio de PHP-FPM: analizar las causas y corregirlas de forma duradera

Architekturdiagramm: Apache über Unix-Socket verbunden mit PHP-FPM, ergänzt durch Logauszug mit AH01079 und Prüfliste
Fehlerursachen an der Schnittstelle Apache ↔ PHP-FPM: Socket-Pfad, Rechte, MAC-Policies und Timeouts kontrollieren.

Código de error 500 en Apache tras el reinicio de PHP-FPM es un caso operativo típico: un reinicio planificado, un parche de seguridad o un despliegue automático — y después los endpoints PHP devuelven de repente HTTP 500 genéricos. En la mayoría de los casos la causa reside en la interfaz de integración entre Apache (servidor web) y PHP-FPM (FastCGI Process Manager). FastCGI es el protocolo para pasar peticiones HTTP a procesos PHP; por socket se entiende aquí tanto un archivo de socket de dominio Unix como un puerto TCP. Los pools son grupos de procesos FPM con ajustes propios.

Código de error 500 en Apache tras el reinicio de PHP-FPM: Causas & procedimiento

HTTP 500 es una respuesta genérica del servidor web y significa: la llamada al backend falló o se abortó. En una configuración con PHP-FPM las causas típicas son: socket/puerto inaccesible, propiedad/permisos incorrectos, Mandatory-Access-Control (SELinux/AppArmor), timeouts en el arranque en frío, o un dimensionamiento de pools inadecuado. Este artículo muestra un orden de comprobaciones priorizado, comandos concretos y medidas sostenibles — con foco en operación, monitorización y rollback.

Verificación rápida: ¿Coinciden el momento y la causa?

Verifique primero si el 500 coincide realmente con el reinicio de PHP-FPM. Si no, puede estar invirtiendo tiempo en el componente equivocado (p. ej. base de datos, red o almacenamiento).

Indicadores típicos de problemas FPM/FastCGI

  • Apache-Error-Log: Mensajes con „proxy_fcgi“, „AH01079“, „AH02454“, „Connection refused“ o „Primary script unknown“.
  • FPM-Logs/journal: ausencia de „ready to handle connections“ o errores de Bind/Listen.
  • Se entregan contenidos estáticos, pero no los endpoints PHP dinámicos.

Orden de comprobaciones pragmático (Runbook)

Trabaje de forma secuencial: servicios/logs → socket/puerto → configuración de Apache → permisos/políticas → recursos/timeouts → parámetros de pool. Documente cada hallazgo en el ticket de incidente.

1) Comprobar servicios y logs en paralelo

journald ofrece indicios rápidos, complementado por el Apache-Error-Log y los archivos de log de FPM.

Shell
systemctl status apache2 --no-pager || systemctl status httpd --no-pager
systemctl status php-fpm --no-pager || systemctl status php8.2-fpm --no-pager
journalctl -u php-fpm -n 200 --no-pager
journalctl -u apache2 -n 200 --no-pager || journalctl -u httpd -n 200 --no-pager

Por qué: Así verá errores de arranque, problemas de bind o denegaciones de permiso de forma inmediata. Copie líneas de log precisas en el ticket.

2) Comprobar socket/puerto

Compruebe si FPM escucha en el endpoint esperado: socket Unix o puerto TCP (p. ej. 127.0.0.1:9000).

Shell
ss -ltnp | grep -E 'php-fpm|:9000' || true
ss -lxnp | grep -E 'php-fpm|fpm' || true
ls -lah /run/php || true
find /run -maxdepth 3 -type s -name '*fpm*.sock' -ls 2>/dev/null | head

Por qué: Después de actualizaciones los caminos pueden cambiar; Apache podría apuntar a un socket antiguo. Si el socket existe, el siguiente paso es comprobar permisos y políticas.

3) Revisar la configuración de Apache

Determine cómo Apache reenvía las llamadas PHP: mod_proxy_fcgi (recomendado) o mod_fcgid. ¿Coinciden las rutas de destino con FPM-listen?

Shell
apache2ctl -M 2>/dev/null | grep -E 'proxy|fcgi' || httpd -M 2>/dev/null | grep -E 'proxy|fcgi'
apache2ctl -S 2>/dev/null || httpd -S 2>/dev/null
grep -R --line-number -E 'proxy_fcgi|SetHandler|FilesMatch|.sock|:9000' /etc/apache2 /etc/httpd 2>/dev/null | head -n 80

Por qué: Con frecuencia un vHost sigue apuntando a un socket antiguo. Los archivos de inclusión configurados de forma central reducen las fuentes de error.

4) Permisos: propiedad del Socket, modos y directorios

Un socket Unix es un archivo con propietario/grupo/modo. Apache se ejecuta, p. ej., como www-data (Debian) o apache (RHEL). Si falta el bit de ejecución en los directorios padre, Apache no puede alcanzar el socket, aunque el socket en sí parezca accesible.

Shell
# Beispiel: Socket prüfen
SOCK="/run/php/php-fpm.sock"  # anpassen
ls -lah "${SOCK}" 2>/dev/null || true
namei -l "${SOCK}" 2>/dev/null || true

# Apache-User ermitteln
ps -eo user,comm | awk '$2 ~ /apache2|httpd/ {print $1}' | sort -u

Por qué: /run es tmpfs; tras un reinicio los directorios de runtime se recrean y requieren ajustes explícitos de propiedad/modo, de lo contrario la conexión no funcionará.

Causas raíz concretas y cómo solucionarlas de forma sostenible

Cambio de ruta del socket (varias versiones de PHP)

Con varias versiones de PHP en un host, el nombre del socket puede cambiar con facilidad. Estabilice las rutas mediante listas claras o asigne la versión de PHP deseada por vHost.

Ini
; Beispiel pool-Konfiguration
listen = /run/php/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Consejo: Evite que varios pools usen el mismo socket — necesitan endpoints propios o puertos TCP.

Permisos incorrectos del socket después del reinicio

Configure listen.owner/listen.group/listen.mode en el archivo del pool; verifique que el usuario de Apache sea miembro del grupo o ajuste el grupo correspondiente.

Shell
# Apache in Gruppe aufnehmen (sorgfältig verwenden)
usermod -aG www-data apache 2>/dev/null || usermod -aG www-data www-data 2>/dev/null
systemctl reload apache2 || systemctl reload httpd

Por qué: Un modo temporal 0666 resuelve problemas de acceso a corto plazo, pero aumenta la superficie de ataque. Es preferible una propiedad explícita y la pertenencia al grupo.

SELinux/AppArmor bloquea conexiones

El control de acceso obligatorio (MAC) como SELinux o AppArmor puede impedir el acceso al socket, incluso cuando los permisos Unix son correctos. Compruebe las entradas AVC o AppArmor-DENIED.

Shell
# SELinux prüfen
getenforce 2>/dev/null || true
ausearch -m avc -ts recent | tail -n 40 || true

# Falls SELinux aktiv ist: Socket-Context setzen
semanage fcontext -a -t httpd_var_run_t '/run/php(/.*)?' || true
RESTorecon -Rv /run/php || true

# AppArmor prüfen
aa-status 2>/dev/null || true
journalctl -k | grep -i apparmor | tail -n 40 || true

Por qué: Las políticas MAC son muy efectivas, pero pueden causar interrupciones de conexión en rutas en tiempo de ejecución sin etiquetas adecuadas. Los cambios en las políticas deben realizarse mediante gestión de cambios; desactivar solo está permitido de forma temporal.

Socket/puerto obsoleto o PID huérfano

A veces queda un archivo de socket o algún proceso ocupa el puerto. Compruébelo con ss/lsof y limpie, pero elimine sockets solo si ningún proceso los está usando.

Shell
ss -ltnp | grep ':9000' || true
lsof /run/php/php-fpm.sock 2>/dev/null || true
# Wenn sicher: rm /run/php/php-fpm.sock && systemctl RESTart php-fpm

Por qué: Borrar sin precaución puede interrumpir conexiones activas. Verifique la información de PID antes.

Timeouts, arranque en frío de OpCache y balanceadores de carga

Tras un reinicio las cachés están vacías; las primeras solicitudes tardan más. Si Apache o un balanceador de carga tienen timeouts demasiado cortos, las solicitudes se interrumpen y los clientes ven 500/502/504.

Shell
# Beispiel: Apache-Timeouts prüfen
apache2ctl -t -D DUMP_RUN_CFG 2>/dev/null | head -n 40 || true
# FPM: slowlog/request_terminate_timeout in Pool-Dateien prüfen
grep -R --line-number -E 'request_terminate_timeout|slowlog' /etc/php* 2>/dev/null | head -n 40

Medidas: aumentar moderadamente los timeouts, ejecutar un warmup de OpCache durante los despliegues y aplicar un procedimiento de reinicio escalonado: dejar que los pools se calienten primero y luego permitir el tráfico.

pm.max_children y gestión de procesos

Si hay demasiado pocos Worker disponibles, la cola se acumula. Tras un reinicio aparecen los cuellos de botella de inmediato porque los Worker se inicializan de nuevo. Dimensione pm.max_children según métricas medidas de memoria y CPU.

Ini
pm = dynamic
pm.max_children = 40
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 16
pm.max_requests = 500

Por qué: un valor demasiado bajo provoca rechazos inmediatos; uno demasiado alto sobrecarga el servidor. Mida el consumo por Worker y pruebe bajo carga.

Primary script unknown: Pfad-/chroot-/open_basedir-Probleme

Si FPM no puede abrir el script objetivo, FPM informa „Primary script unknown“ y Apache devuelve 500. Compruebe DocumentRoot, la ruta del proxy, la configuración de chroot o las RESTricciones de open_basedir.

Práctica: Troubleshooting-Skripte und Runbook-Snippets

Integración con systemd: RuntimeDirectory und tmpfiles.d

Systemd puede asegurarse de que /run/php exista con los permisos correctos antes del arranque. Use RuntimeDirectory en el archivo de unidad o una configuración tmpfiles.d.

Shell
# Beispiel systemd-Override (speichern unter /etc/systemd/system/php-fpm.service.d/10-run.conf)
[Service]
RuntimeDirectory=php
RuntimeDirectoryMode=0755
RuntimeDirectoryPreserve=yes

# tmpfiles.d Alternative (z. B. /etc/tmpfiles.d/php-fpm.conf)
# d /run/php 0755 www-data www-data -

Por qué: así el directorio siempre se crea con el propietario/modo correctos antes del inicio del servicio — evita condiciones de carrera (race-conditions) durante el arranque/reinicio.

Fallback auf TCP: Konfiguration & Hinweise

Como bypass de diagnóstico a corto plazo, cambiar de socket Unix a TCP (127.0.0.1:9000) puede ayudar, ya que se evitan muchos problemas de permisos y de MAC. Sin embargo, a largo plazo TCP introduce latencia, un control de acceso menos fino y posibles colisiones de puertos.

Ini
; php-fpm pool.conf
; unix socket
;listen = /run/php/php-fpm.sock
; TCP alternative
listen = 127.0.0.1:9000
Shell
# Apache ProxyPassMatch Beispiel TCP
# In vHost

  SetHandler "proxy:fcgi://127.0.0.1:9000"

Cuándo falla esto: cuando los firewalls de la infraestructura tengan políticas TCP locales o cuando varios procesos estén usando el puerto.

Script de warmup de OpCache (ejemplo sencillo)

Un script de warmup puede, durante el despliegue o un reinicio, rellenar las entradas de OpCache y así reducir las latencias de cold start.

Shell
#!/bin/bash
# opcache-warmup.sh - ruft eine Liste relevanter URLs sequentiell ab
URLS=( "/" "/login" "/app/home" )
HOST="https://localhost"
for u in "${URLS[@]}"; do
  curl -ksS --fail "${HOST}${u}" >/dev/null || echo "Warmup failed for ${u}"
  sleep 0.5
done

Por qué: reduce los picos de carga y los timeouts en las primeras solicitudes de usuario tras un reinicio. Pruebe esto en staging.

Script de rollback rápido: alternar Socket ↔ TCP

Shell
#!/bin/bash
# rollback-to-tcp.sh - cambia el pool a TCP y recarga los servicios
POOL_CONF="/etc/php/8.2/fpm/pool.d/www.conf"
cp ${POOL_CONF} ${POOL_CONF}.bak.$(date +%s)
sed -i 's|listen = /run/php/php-fpm.sock|listen = 127.0.0.1:9000|' ${POOL_CONF}
systemctl RESTart php-fpm && systemctl reload apache2 || systemctl RESTart httpd

Por qué: Un rollback controlado minimiza el tiempo de inactividad. Pruebe el script en un entorno seguro antes de usarlo en producción.

Monitorización, alertas y prevención

Complete la monitorización genérica de 5xx con indicadores específicos: patrones en el registro de errores de Apache (proxy_fcgi), estadísticas del pool FPM (active processes, listen queue), alertas de pm.max_children y métricas del sistema (RAM/IO). Así podrá distinguir efectos de arranque en frío de cuellos de botella estructurales.

Recomendaciones para alertas

  • Alerta ante líneas AH01079/AH02454 repetidas en el registro de errores de Apache en un breve período.
  • FPM: alerta si listen queue > 0 de forma sostenida o si aparece „reached pm.max_children“ en los registros.
  • Sistema: alta utilización de swap/I/O o entradas del OOM-Killer inmediatamente antes de errores 500.

Post-Incident: análisis de causa raíz y medidas preventivas

Tras una mitigación temporal, realice siempre un RCA estructurado: ¿Qué cambio provocó el fallo? ¿Fue una actualización, un cambio de configuración o una condición de carrera en el arranque? Ajuste la gestión de configuraciones (Git), las plantillas de cambio para pools y los overrides de systemd, y documente las lecciones aprendidas en el runbook.

Lista de verificación para un ticket de incidente (compacta)

  1. Hora del reinicio y duración del incidente
  2. Líneas exactas del registro de errores de Apache
  3. Extracto del journal de FPM en torno al reinicio
  4. Salida de ss/ls/find para socket/puerto
  5. Salida de namei -l de la ruta del socket
  6. SELinux/estado de AppArmor y líneas relevantes AVC/deny
  7. Métricas breves de memoria/I/O/CPU
  8. Si cambiar a TCP ayudó (sí/no)

Prácticas operativas y conocimiento de operación

  • Versione los includes de pool y de Apache en Git; despliegues solo mediante CI con pruebas.
  • Use RuntimeDirectory de systemd o tmpfiles.d para que /run/php se prepare correctamente.
  • Considere etiquetas para SELinux temprano en el proceso de cambio, no de manera ad-hoc.
  • Implemente healthchecks y scripts de warmup para los reinicios.
  • Pruebe los scripts de rollback antes de documentarlos como herramienta de emergencia.

Conclusión

Un error 500 tras el reinicio de PHP-FPM es, en la mayoría de los casos, un problema de integración en la interfaz Apache ↔ PHP-FPM: socket/puerto, Ownership/Mode, políticas MAC, timeouts y dimensionado del pool son las causas típicas. La solución sostenible combina un análisis de errores inmediato, guiado por logs, con medidas permanentes: rutas de socket estables, Ownership explícito en los archivos de pool, integración con systemd para directorios de runtime, etiquetas SELinux/AppArmor adecuadas, procedimientos de warmup y monitorización dirigida. Además, los mecanismos de rollback probados y los runbooks documentados reducen el riesgo de incidentes secundarios.

Utilice los pasos de verificación anteriores como plantilla de incidente y realice tras cada fallo un RCA y un ajuste de políticas. Así, el próximo reinicio de PHP-FPM volverá a ser una operación de rutina.

Aspectos operativos: orquestación, healthchecks y contenedores

Los reinicios automatizados mediante systemd, Configuration-Management u orquestadores pueden provocar condiciones de carrera cuando los balanceadores de carga o los frontends no se vacían (drained). Planifique reinicios escalonados con fases de vaciado (drain) y comprobaciones de estado (readiness/liveness) para que las sesiones y las conexiones entrantes se cierren de forma controlada.

Systemd-Socket-Activation puede ser aquí un componente estable: la unidad de sockets mantiene el endpoint a lo largo de los ciclos de reinicio y reduce los casos de „Connection refused“. PRESTe atención, eso sí, a los directorios de runtime y a la persistencia en tmpfs.

En entornos con contenedores, los Unix-sockets sobre OverlayFS o en volúmenes montados son más susceptibles a problemas de permisos e inodos; por ello, compruebe los TCP-Bindings o volúmenes compartidos dedicados con reglas claras de ownership.

Mejore la observabilidad: correlacione los logs de Apache y FPM mediante Request‑IDs y exporte las métricas del pool (listen‑queue, active) para habilitar alertas tempranas.

Para este tema también son relevantes Apache Http 500 y Php-Fpm Socket. La entrada contextualiza estos aspectos de forma comprensible y muestra en qué debe centrarse la operación diaria.