La forma más rápida de tumbar un sitio WordPress sano no es un plugin malo. Es un cambio “pequeño” en wp-config.php hecho bajo presión: el host de BD cambiado, una bandera de debug dejada activada, una cabecera de proxy inverso asumida, un drop-in de caché medio instalado.
Puesto que wp-config.php se carga temprano en la ruta de arranque, los errores aquí no degradan elegantemente. Fallan de manera ruidosa, o peor: funcionan a trompicones mientras filtran información, omiten cron, o escriben logs hasta que tu disco se agota.
Qué hace realmente wp-config.php (y por qué los errores dañan)
wp-config.php no es sólo “donde vive la contraseña de la base de datos.” Es el panel de control de cómo arranca WordPress: de dónde lee datos, qué asume sobre el esquema/host de la petición, cómo registra, qué capa de caché intenta cargar y qué tokens de seguridad confía.
La mayoría de los archivos de configuración se leen tarde, después de que el framework se inicializa. WordPress lee wp-config.php temprano y con frecuencia. Eso tiene dos consecuencias operativas:
- Los modos de fallo son inmediatos. Errores de sintaxis, constantes erróneas, rutas de include equivocadas: obtienes pantallas blancas, 500 o “Error establishing a database connection”.
- El mal funcionamiento puede ser sutil. El sitio “funciona”, pero cron no se ejecuta, las cookies de administración se invalidan, aparece contenido mixto, el object cache entra en thrash, o tus logs se llenan a un ritmo que impresiona a un bot DDoS.
Principios que te mantienen fuera de problemas
- Haz los cambios reversibles. Si no puedes revertirlo en segundos, no estás “configurando”, estás apostando.
- Prefiere secretos impulsados por el entorno. Cuantas menos credenciales hardcodeadas, menos probabilidad de que acaben en un repo git, un archivo de backup o un pastebin trágico.
- No trates wp-config.php como un vertedero. Es tentador. Resiste. Si defines diez constantes no relacionadas para parchear el comportamiento de plugins, estás construyendo un altar para futuras caídas.
Una pequeña señal de advertencia: cuando un equipo dice “es sólo un ajuste de configuración”, normalmente es cuando deberías programar una ventana de mantenimiento.
Broma 1: wp-config.php es como una caja para romper cristal en emergencias. Si la abres con despreocupación, ya estás teniendo un mal día.
Guion rápido de diagnóstico
Este es el flujo de “detener la hemorragia”. Está sesgado hacia producción: cambios mínimos, máxima señal. Haz esto en orden salvo que ya tengas una pistola humeante.
Primero: confirma la clase de fallo (parseo PHP vs WordPress vs infra)
- Revisa el estado HTTP y el cuerpo de la respuesta. Un 500 con cuerpo vacío a menudo significa fatal/parse de PHP; “Error establishing a database connection” es WordPress capturando fallos de conexión a BD.
- Tail de logs de PHP-FPM / servidor web. Si ves “Parse error” referenciando
wp-config.php, para y corrige la sintaxis antes de cualquier otra cosa. - Valida que PHP pueda leer el archivo. Permisos equivocados pueden parecer comportamiento de “config faltante”.
Segundo: valida la conectividad a la base de datos desde el host de la app
- Resuelve el host de BD. La deriva DNS causa outages reales.
- Conéctate con las mismas credenciales. Demuestra que el usuario/contraseña y la ruta de red son válidos.
- Confirma expectativas de charset/collation. Valores mal ajustados no siempre bloquean el arranque, pero pueden corromper datos con el tiempo o romper migraciones.
Tercero: evalúa las suposiciones sobre esquema/host de la petición
- ¿Detrás de un proxy inverso? Si las cabeceras en
$_SERVERno son confiables, obtienes bucles de redirección, contenido mixto o fallos de login en admin. - Revisa
WP_HOMEyWP_SITEURL. Un carácter equivocado puede dejar tu admin atrapado en redirecciones interminables.
Cuarto: busca “interruptores útiles” que se volvieron pasivos
- Flags de debug.
WP_DEBUGactivado en producción es un impuesto de rendimiento y riesgo de filtrado de datos. - Drop-ins de caché. Misconfiguraciones de object cache Redis/Memcached llevan a rarezas en login y picos de CPU que se hacen pasar por “WordPress lento”.
- Configuraciones de cron. Deshabilitar WP-Cron sin un cron del sistema real es cómo dejan de enviarse newsletters y nadie lo nota hasta que marketing lo detecta.
Si sólo recuerdas una cosa: prueba lo básico primero—sintaxis, permisos, alcance a BD—antes de debatir filosofía de caché.
Hechos e historia útiles
- Hecho 1: Históricamente WordPress usaba
wp-config-sample.phpcomo plantilla; la gente todavía lo copia literalmente, incluyendo suposiciones obsoletas sobre salts, debug y valores por defecto de DB host. - Hecho 2: La configuración de
$table_prefixexiste en parte porque WordPress previó bases de datos compartidas y múltiples instalaciones en un mismo esquema—común en hosting económico temprano. - Hecho 3: Los “drop-ins” de WordPress (como
object-cache.php) se cargan antes que la mayoría de los plugins. Un drop-in roto puede romper todo mientras la lista de plugins parece inocente. - Hecho 4: La configuración
define('WP_CACHE', true)existe en gran medida para coordinar con plugins de caché, pero activarla sin el resto de la pila es una clásica instalación a medias. - Hecho 5:
DISABLE_WP_CRONfue una respuesta pragmática a la programación dependiente del tráfico. En sitios de poco tráfico, WP-Cron es básicamente “quizá después”. - Hecho 6: Muchos hosts ejecutan PHP como un usuario diferente (pool FPM) al de tu usuario SSH. Permisos de archivos que “se ven bien” en tu shell pueden ser inaccesibles para PHP.
- Hecho 7:
FORCE_SSL_ADMINprecede la norma moderna de “todo tras TLS”. Aún puede importar cuando tienes terminación TLS dividida o capas de proxy mezcladas. - Hecho 8: WordPress puede tomar constantes de BD desde variables de entorno. Esto no es solo moda “12-factor”; es una forma real de evitar comprometer secretos en imágenes o repos.
- Hecho 9: La ubicación canónica de
wp-config.phppuede estar un directorio por encima del document root. Esto es hardening de la vieja escuela, y aún funciona.
La mayoría de estas funciones nacieron de limitaciones de hosting compartido y luego se trasladaron a producción seria. Por eso wp-config.php puede sentirse como una excavación arqueológica.
Tareas prácticas: comandos, salidas y decisiones
Estas son tareas “ejecuta ahora”. Cada una incluye: el comando, qué significa la salida y qué decisión tomar después. Están escritas para un host Linux típico con Nginx/Apache + PHP-FPM + MySQL/MariaDB, pero la lógica aplica en otros entornos.
Tarea 1: Verificar que wp-config.php existe y no es un symlink colgante
cr0x@server:~$ ls -la /var/www/html/wp-config.php
-rw-r----- 1 www-data www-data 3890 Dec 27 10:11 /var/www/html/wp-config.php
Qué significa: El archivo existe, es regular y es legible por el grupo del usuario web. Si ves wp-config.php -> /some/path, confirma que el destino existe.
Decisión: Si falta o no es legible, arregla la ruta/permisos antes de cualquier otra cosa. WordPress no puede “salvar” una config ausente.
Tarea 2: Revisar permisos (y atrapar la trampa “usuario SSH vs usuario PHP”)
cr0x@server:~$ namei -l /var/www/html/wp-config.php
f: /var/www/html/wp-config.php
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-xr-x root root html
-rw-r----- www-data www-data wp-config.php
Qué significa: Los bits de ejecución del directorio permiten el tránsito; el archivo final es legible. Si algún directorio en la ruta carece de execute para el usuario PHP, PHP no puede alcanzar el archivo.
Decisión: Corrige permisos de recorrido de directorios en lugar de “chmod 777” a todo. Si estás pensando en 777, aléjate, toma café y luego hazlo correctamente.
Tarea 3: Validar que wp-config.php no tenga errores de parseo PHP
cr0x@server:~$ php -l /var/www/html/wp-config.php
No syntax errors detected in /var/www/html/wp-config.php
Qué significa: PHP puede parsearlo. Fallos comunes: BOM suelto, punto y coma faltante, comillas “inteligentes” copiadas de un ticket, o un <?php extra.
Decisión: Si aparecen errores de sintaxis, arréglalos primero. Todo lo demás es ruido hasta que el parseo tenga éxito.
Tarea 4: Confirmar que las constantes de BD están presentes (sin imprimir secretos ampliamente)
cr0x@server:~$ sudo -u www-data php -r 'include "/var/www/html/wp-config.php"; echo DB_NAME, "\n", DB_HOST, "\n";'
wordpress_prod
db01.internal
Qué significa: La config se carga bajo el mismo usuario que ejecuta PHP y las constantes resuelven. Si el include falla, verás warnings/fatales.
Decisión: Si esto falla, enfócate en permisos/rutas/includes. Si funciona, pasa a la conectividad de red/BD.
Tarea 5: Verificar resolución DNS para DB_HOST
cr0x@server:~$ getent hosts db01.internal
10.20.30.41 db01.internal
Qué significa: El host resuelve. Si no lo hace, WordPress puede intentar y fallar repetidamente, llenando procesos PHP.
Decisión: Si falla DNS, arregla DNS/archivo hosts o usa temporalmente una IP (con plan para revertir). No “arregles” WordPress cuando el problema es DNS.
Tarea 6: Comprobar conectividad TCP al puerto de la base de datos
cr0x@server:~$ nc -vz db01.internal 3306
Connection to db01.internal 3306 port [tcp/mysql] succeeded!
Qué significa: La ruta de red está abierta. Si obtienes timeouts/connection refused, es firewall, security group, BD caída o puerto equivocado.
Decisión: Si TCP falla, no rotees contraseñas. Arregla la conectividad de red primero.
Tarea 7: Autenticar a MySQL usando las mismas credenciales que WordPress usa
cr0x@server:~$ mysql -h db01.internal -u wp_user -p -e "SELECT 1;"
Enter password:
1
1
Qué significa: Las credenciales funcionan y el servidor BD responde. Si la autenticación falla, el mensaje de error es oro: “Access denied”, “Unknown database”, etc.
Decisión: Si falla la autenticación, verifica DB_USER, DB_PASSWORD y grants. Si el nombre de la BD es incorrecto, corrige DB_NAME en lugar de crear un esquema vacío “para que funcione”.
Tarea 8: Identificar esquema/host de la petición desde el borde (sanidad de proxy inverso)
cr0x@server:~$ curl -I http://127.0.0.1 | sed -n '1,10p'
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 27 Dec 2025 10:20:04 GMT
Content-Type: text/html
Content-Length: 162
Location: https://example.com/
Qué significa: Estás viendo una redirección. Si estás detrás de un proxy que termina TLS, el origen puede pensar que es HTTP y redirigir incorrectamente.
Decisión: Si tienes bucles de redirección, revisa WP_HOME/WP_SITEURL y el manejo de cabeceras de proxy antes de tocar plugins.
Tarea 9: Comprobar bucle de redirección desde el host público
cr0x@server:~$ curl -sS -o /dev/null -D - https://example.com/wp-admin/ | sed -n '1,12p'
HTTP/2 302
date: Sat, 27 Dec 2025 10:21:11 GMT
content-type: text/html; charset=UTF-8
location: https://example.com/wp-login.php?redirect_to=https%3A%2F%2Fexample.com%2Fwp-admin%2F&reauth=1
set-cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/; secure; HttpOnly
Qué significa: Un flujo normal de admin redirige al login. Si rebotas repetidamente entre http/https o distintos hostnames, la config está mintiendo sobre la URL canónica.
Decisión: Corrige URLs canónicas en la config (o en la BD) y ajusta la detección de esquema del proxy. No “soluciones” con un plugin de redirect aleatorio.
Tarea 10: Confirmar modo debug y si los logs crecen
cr0x@server:~$ grep -nE "WP_DEBUG|WP_DEBUG_LOG|WP_DEBUG_DISPLAY" /var/www/html/wp-config.php
92:define('WP_DEBUG', false);
93:define('WP_DEBUG_LOG', true);
94:define('WP_DEBUG_DISPLAY', false);
Qué significa: Debug está apagado, pero el registro está activado. Esa combinación a veces es intencional, pero aún puede escribir mucho si los plugins emiten notices.
Decisión: Si el uso de disco sube, arregla el código ruidoso o deshabilita el log de debug en producción y enruta errores a PHP-FPM/syslog con rotación adecuada.
Tarea 11: Vigilar tamaño de logs y presión de rotación (el outage favorito del ingeniero de almacenamiento)
cr0x@server:~$ sudo du -sh /var/www/html/wp-content/debug.log
3.6G /var/www/html/wp-content/debug.log
Qué significa: Ese archivo es enorme. En un volumen raíz pequeño, así es como “el sitio web está caído” se convierte en “el servidor está caído”.
Decisión: Si el log es grande, detén la amplificación de escritura: desactiva debug logging, trunca de forma segura y añade rotación. También investiga por qué creció (a menudo un notice repetido en un hot path).
Tarea 12: Revisar espacio libre en el sistema de archivos (porque errores de config se reflejan en discos)
cr0x@server:~$ df -h /var/www
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 40G 39G 600M 99% /
Qué significa: Estás a un deploy de distancia del downtime. Al 99%, MySQL y PHP-FPM empiezan a fallar de maneras creativas.
Decisión: Libera espacio inmediatamente (logs, releases antiguos, caches), luego arregla la causa subyacente (a menudo debug logs, backups en el mismo volumen o directorios de caché fuera de control).
Tarea 13: Verificar drop-in de object cache y si WordPress intenta usarlo
cr0x@server:~$ ls -la /var/www/html/wp-content/object-cache.php
-rw-r--r-- 1 www-data www-data 14523 Dec 27 09:42 /var/www/html/wp-content/object-cache.php
Qué significa: Existe un drop-in. Eso significa que WordPress lo cargará temprano. Si está roto o apunta a un host Redis caído, puedes obtener admin lento, fallos de login o errores fatales.
Decisión: Si sospechas problemas de caché, muévelo temporalmente fuera del camino (renombra el archivo) durante una ventana de mantenimiento y vuelve a probar.
Tarea 14: Probar conectividad Redis (si lo configuraste en wp-config.php)
cr0x@server:~$ redis-cli -h 127.0.0.1 ping
PONG
Qué significa: Redis es alcanzable y responde. Si obtienes timeout, está caído, escucha en otro sitio, protegido por ACL o bloqueado por firewall.
Decisión: Si Redis es inalcanzable, deshabilita la integración de object cache hasta que esté arreglado. Caches rotos son peores que no tener caché.
Tarea 15: Confirmar límite de memoria de PHP y si WordPress lo está sobreescribiendo
cr0x@server:~$ php -i | grep -i "^memory_limit"
memory_limit => 256M => 256M
Qué significa: El límite de memoria base de PHP es 256M. WordPress puede solicitar más vía constantes como WP_MEMORY_LIMIT, pero no puede exceder el tope duro de PHP si está configurado así.
Decisión: Si ves “Allowed memory size exhausted”, aumenta el límite de PHP-FPM (y luego encuentra qué consume memoria; no aumentes límites eternamente).
Tarea 16: Validar URLs canónicas de WordPress en la base de datos cuando la config no las define
cr0x@server:~$ mysql -h db01.internal -u wp_user -p -D wordpress_prod -e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
Enter password:
option_name option_value
home https://example.com
siteurl https://example.com
Qué significa: Los valores en BD son consistentes. Si difieren (o contienen el host/esquema equivocado), obtendrás bucles de redirección, URLs de assets rotas y dolor en admin.
Decisión: Si están mal, arréglalos con cuidado. Prefiere una actualización controlada y luego limpia caches. No “hardcodees” constantes temporalmente y las olvides por años.
Tarea 17: Revisar comportamiento de cron si DISABLE_WP_CRON está establecido
cr0x@server:~$ grep -n "DISABLE_WP_CRON" /var/www/html/wp-config.php
110:define('DISABLE_WP_CRON', true);
Qué significa: WP-Cron está deshabilitado. Eso está bien sólo si tienes un cron del sistema real que llame a wp-cron.php.
Decisión: Si no tienes un cron del sistema, vuelve a habilitar WP-Cron o añade un job de cron inmediatamente. Si no, las tareas programadas, limpiezas y trabajos en segundo plano se quedan estancados sin aviso.
Errores comunes: síntoma → causa raíz → solución
Esta es la sección a la que volverás a las 02:00. Está diseñada para mapear lo que ves con lo que realmente está mal y darte una solución concreta.
1) Pantalla blanca / HTTP 500 justo después de editar wp-config.php
Síntomas: Página en blanco, error 500 o “There has been a critical error” inmediatamente tras un cambio de configuración.
Causa raíz: Error de sintaxis PHP (punto y coma faltante, comilla suelta), BOM al inicio del archivo o tags <?php duplicados.
Solución: Ejecuta php -l sobre el archivo; revierte a la última versión conocida buena. Si copiaste/pegaste desde un ticket, revisa por “smart quotes”.
2) “Error establishing a database connection”
Síntomas: Front-end y admin de WordPress muestran la página de error de BD; las comprobaciones de salud fallan.
Causa raíz: DB_HOST equivocado, credenciales erróneas, privilegios faltantes, problema de resolución DNS o servidor BD caído.
Solución: Demuestra conectividad de red (nc) y autentica con mysql usando las mismas credenciales. Rotea credenciales sólo después de confirmar que la BD es accesible y está sana.
3) Bucles de redirección (especialmente después de añadir un proxy inverso/CDN)
Síntomas: El navegador dice “too many redirects”, el login de admin rebota para siempre o HTTP/HTTPS se alterna repetidamente.
Causa raíz: WordPress cree que las peticiones son HTTP cuando son HTTPS en el borde; WP_HOME/WP_SITEURL desajustados; cabeceras de proxy no confiadas.
Solución: Establece URLs canónicas de forma coherente y maneja HTTP_X_FORWARDED_PROTO sólo cuando proviene de proxies de confianza. No confíes ciegamente cabeceras reenviadas desde Internet.
4) Advertencias de contenido mixto y CSS/JS roto
Síntomas: Las páginas cargan pero los assets son bloqueados; admin aparece sin estilos; la consola muestra errores de contenido mixto.
Causa raíz: siteurl/home o WP_HOME/WP_SITEURL usando http:// mientras el sitio se sirve por https://.
Solución: Estandariza en HTTPS en config y BD. Si estás detrás de un proxy, corrige la detección de esquema para que WordPress genere URLs HTTPS.
5) Información de debug filtrándose en páginas
Síntomas: Visitantes ven warnings/notices; el HTML incluye rutas de archivos y detalles de consultas.
Causa raíz: WP_DEBUG activado con WP_DEBUG_DISPLAY en true, o display_errors de PHP habilitado a nivel FPM.
Solución: En producción: WP_DEBUG false, WP_DEBUG_DISPLAY false. Registra errores en otro sitio con rotación. Si necesitas debug, habilítalo temporalmente y con límite de tiempo.
6) Disco se llena inesperadamente
Síntomas: El servidor pasa a modo solo lectura o servicios fallan; df muestra 95–100% de uso.
Causa raíz: WP_DEBUG_LOG generando un enorme wp-content/debug.log; un plugin spameando notices; o una caché mal configurada que escribe sin control.
Solución: Detén el crecimiento del log (deshabilita debug log), trunca el archivo de forma segura, añade rotación y arregla la ruta de código ruidosa.
7) Cookies de login inválidas o sesiones de admin que expiran aleatoriamente
Síntomas: Inicias sesión y te desconectan inmediatamente; aparecen errores “Are you sure you want to do this?” por fallos de nonces.
Causa raíz: Cambio de salts/keys (o rotación demasiado agresiva), COOKIE_DOMAIN inconsistente, proxy inverso provocando cambios de host o caching de páginas de admin.
Solución: Mantén salts estables salvo que quieras invalidar sesiones intencionalmente. No cachees /wp-admin ni páginas con usuarios logueados en el edge. Asegura un host canónico y dominio de cookie único.
8) Object cache habilitado pero el rendimiento empeora
Síntomas: Mayor TTFB, más CPU, admin lento; errores intermitentes en logs del backend de caché.
Causa raíz: Redis/Memcached inalcanzable/lento, socket/host equivocado, desajuste de autenticación o drop-in de caché incompatible con la versión de PHP.
Solución: Valida conectividad al backend, mide latencia y deshabilita el drop-in hasta que sea estable. Un cache que falla con timeouts es un DoS autoinfligido.
9) Multisite se comporta raro tras una migración
Síntomas: Subsites redirigen mal; uploads faltan; network admin roto.
Causa raíz: Constantes de multisite desajustadas (MULTISITE, SUBDOMAIN_INSTALL, DOMAIN_CURRENT_SITE, constantes de path), o URLs en BD obsoletas.
Solución: Trata la config de multisite como una unidad. Valida constantes de dominio/path contra la BD y el enrutamiento del proxy inverso.
10) Regresión de seguridad: credenciales de BD expuestas o config escribible
Síntomas: Credenciales descubiertas en un repo, backups o archivos legibles por todo el mundo; malware edita la config.
Causa raíz: Permisos débiles, secretos comprometidos en commits o config en document root con servidor mal configurado que permite descargar el código fuente (raro, pero catastrófico).
Solución: Asegura permisos, mantiene secretos fuera de repos, considera mover wp-config.php un directorio arriba y verifica que el servidor nunca sirva el código PHP.
Broma 2: Dejar WP_DEBUG activado en producción es como dejar el capó del coche abierto “para mejorar la refrigeración”. Aprenderás nuevos sonidos.
Tres microhistorias corporativas de producción
Microhistoria 1: El incidente causado por una suposición equivocada
La empresa estaba migrando WordPress de una VM única a una “configuración correcta”: base de datos gestionada, un balanceador de carga y dos nodos de aplicación. El plan de migración parecía sólido. El cambio en la config parecía inocuo: poner DB_HOST al endpoint gestionado y desplegar.
El outage llegó en minutos. Ambos nodos devolvían “Error establishing a database connection.” Alguien juró que las credenciales eran correctas porque “funcionaban en su portátil.” Esa frase debería imprimirse en una etiqueta de advertencia y pegarse a todos los teléfonos on-call.
La causa raíz no fue la contraseña. Fue la resolución de nombres dentro de la VPC. El endpoint gestionado resolvía a una dirección privada, pero los nodos app apuntaban a un resolvedor distinto que no conocía esa zona privada. El endpoint no resolvía de forma consistente, así que los workers PHP se amontonaron reintentando conexiones. Las comprobaciones de salud fallaron. El balanceador drenó los nodos. Outage total, aunque “la BD estaba arriba”.
La solución fue aburrida: corregir la configuración DNS y probar la resolución desde la subred de la app antes de cualquier cambio de config. Añadieron un script preflight que ejecuta getent hosts y una prueba de conexión TCP desde cada nodo durante el deploy. La siguiente migración fue suave, porque dejaron de asumir que “DNS funcionará”.
Microhistoria 2: La optimización que salió mal
Llegó un empujón de rendimiento con la música de fondo ejecutiva habitual: “Necesitamos el sitio más rápido.” Ingeniería respondió con caching de objetos en Redis. Razonable. Añadieron el drop-in, pusieron WP_CACHE y definieron un host Redis en la config. Las pruebas de carga mejoraron. Todos se felicitaron.
Entonces llegó el tráfico de producción. La latencia empeoró, no mejoró. La CPU subió en los nodos app. Los contadores de workers PHP aumentaron. La carga de la base de datos bajó, lo que parecía bueno hasta que notaste que el sitio estaba más lento. Una de esas semanas en que “optimizamos lo incorrecto”.
El problema real: Redis era accesible, pero vivía tras un salto de red que añadía picos de latencia ocasionales. El drop-in del object-cache estaba configurado con un timeout de conexión que era generoso en términos humanos y desastroso en términos de ruta de petición. Cuando Redis se atascaba, cada petición esperaba. Peor aún, la penalidad de cache miss disparaba más trabajo PHP mientras workers esperaban en la caché. Así es como el “cache” se convierte en un mecanismo de limitación distribuida.
La solución fue tratar Redis como infraestructura de producción, no como una casilla de plugin. Movieron Redis más cerca (o a la misma capa de red), endurecieron timeouts y monitorizaron latencia de caché como si fuera una base de datos. Cuando Redis estaba malsano, prefirieron fallar rápido en abierto en vez de esperar. Los beneficios de rendimiento volvieron—porque la caché dejó de ser la dependencia más lenta.
Microhistoria 3: La práctica aburrida y correcta que salvó el día
Un equipo distinto ejecutaba WordPress como propiedad crítica para ingresos. No eran llamativos. Eran meticulosos. Cada cambio de config se desplegaba vía pipeline que realizaba tres comprobaciones: lint de sintaxis, chequeo de permisos bajo el usuario web y una probe de conectividad a BD usando el host y usuario configurados.
Una tarde, una rotación de credenciales rutinaria salió mal. El secrets manager actualizó, pero un nodo app no refrescó sus variables de entorno debido a un bug del supervisor de procesos. La mitad del cluster tenía nuevas credenciales, la otra mitad no. El síntoma fue feo: fallos intermitentes de conexión según el nodo que el balanceador escogiera.
El pipeline lo detectó antes de convertirse en un outage total. La probe de BD falló en un nodo y el deploy se detuvo. El on-call recibió un error claro: “Cannot authenticate with configured credentials from node X.” Drenaron ese nodo, reiniciaron servicios para recoger la nueva env y lo reintrodujeron. Los clientes no notaron nada.
La lección no fue “usa variables de entorno.” Fue: prueba la config tal como la usa producción, cada vez. Las comprobaciones aburridas previenen incidentes emocionantes.
idea parafraseada — John Allspaw: la fiabilidad viene de diseñar para cómo fallan los sistemas, no de esperar que personas cuidadosas no cometan errores.
Listas de verificación / plan paso a paso
Paso a paso: editar wp-config.php de forma segura en producción
- Haz una copia con permisos preservados.
cr0x@server:~$ sudo cp -a /var/www/html/wp-config.php /var/www/html/wp-config.php.bakSignificado:
-apreserva modo/propiedad/timestamps. Tu rollback no creará un nuevo problema de permisos.Decisión: Si no puedes hacer backup, no estás listo para editar.
- Edita con una herramienta que no añada BOM o comillas inteligentes. Usa
vim,nanoo un editor sensato sobre SSH. - Haz lint antes del reload.
cr0x@server:~$ php -l /var/www/html/wp-config.php No syntax errors detected in /var/www/html/wp-config.phpDecisión: Si el lint falla, revierte inmediatamente y edita con calma.
- Valídalo bajo el usuario web.
cr0x@server:~$ sudo -u www-data php -r 'include "/var/www/html/wp-config.php"; echo "loaded\n";' loadedDecisión: Si esto falla, probablemente introdujiste un problema de ruta/include/permiso que tu shell root oculta.
- Golpea un endpoint ligero y mira logs. Prefiere una petición única + tail de logs en lugar de una tormenta de refrescos desde el navegador.
Checklist de endurecimiento (haz esto, no discutas)
- Permisos de archivos: legible por PHP, no legible por todo el mundo. Típico:
640propiedad dewww-datao root con grupo legible. - Mueve wp-config.php un directorio arriba cuando el layout lo permita, para que no esté dentro del document root.
- Deshabilita display de debug en producción; registra en una ubicación controlada con rotación.
- Rota salts intencionalmente (respuesta a incidentes), no de forma casual. Rotar salts cierra sesión a todos los usuarios e invalida sesiones.
- No confíes en cabeceras reenviadas por defecto. Sólo respétalas desde IPs de proxy conocidas.
- Mantén
WP_HOME/WP_SITEURLconsistentes. Si las defines en config, documenta por qué y dónde está la fuente de la verdad. - Confirma la estrategia de cron: o bien WP-Cron está habilitado, o hay un cron del sistema en su lugar. “Deshabilitado sin reemplazo” no es una estrategia.
Checklist de rendimiento (la versión que no te sabotea)
- Mide antes de habilitar object cache. Si tu cuello de botella es CPU PHP o plugins lentos, Redis no te salvará.
- Establece timeouts agresivos para caches. Un cache que bloquea peticiones es peor que no tener cache.
- Mantén el logging de debug apagado en hot paths. I/O de disco no es una misión secundaria gratis.
- Alinea límites de memoria con la realidad. Aumenta límites sólo si también arreglas la causa (plugins, queries gigantes, picos de procesamiento de imágenes).
Qué poner en wp-config.php (y qué no)
Configuración de base de datos: correcta, mínima, testeable
Como mínimo: DB_NAME, DB_USER, DB_PASSWORD, DB_HOST. Todo lo demás debe estar justificado. No esparzas constantes experimentales porque un post de blog lo dijo. Los blogs no reciben páginas de on-call.
Salts y claves: no improvises criptografía
Las claves y salts protegen cookies y nonces. Si cambian, las sesiones se invalidan. Eso puede ser intencional durante respuesta a incidentes. No debería formar parte de un deploy casual. Guárdalas de forma segura y mantenlas estables.
Prefijo de tablas: no es una panacea de seguridad
Cambiar $table_prefix de wp_ está bien, pero no lo sobrevalores. Es una defensa débil frente a SQL injection; la solución real es no tener vulnerabilidades de inyección. Aun así, prefijos únicos ayudan al alojar múltiples sitios en una BD y reducen colisiones accidentales durante migraciones.
Proxy inverso y manejo de HTTPS: sé explícito y cauteloso
Muchos outages vienen de una línea que fuerza HTTPS incorrectamente o que confía en X-Forwarded-Proto de cualquiera. Si debes sobrescribir la detección de esquema en wp-config.php, hazlo con listas de IPs permitidas en el servidor web/proxy, no con buena fe dentro de PHP.
Ajustes de debug: elige “registro seguro” o “caos temporal”, no ambos
En producción, el patrón limpio es: no mostrar errores; registrar errores en una ubicación controlada; rotar logs; alertar en picos de tasa. Si necesitas debugging verboso, limita el tiempo y ten un plan de rollback.
Actualizaciones automáticas y modificaciones de archivos: entiende el radio de impacto
Deshabilitar ediciones de archivos y controlar actualizaciones puede ser responsable en entornos regulados. También puede dejarte corriendo plugins vulnerables por meses porque “las actualizaciones dan miedo”. Decide según la madurez de tu pipeline de parches, no por superstición.
Preguntas frecuentes (FAQ)
1) ¿Puedo almacenar credenciales de BD en variables de entorno en vez de en wp-config.php?
Sí, y a menudo es mejor operativamente. Reduces la probabilidad de cometer secretos. Pero debes asegurar que el servicio PHP-FPM reciba confiablemente el entorno actualizado en deploy/restart, y necesitas una vía clara de rollback.
2) ¿Debe wp-config.php pertenecer a root o a www-data?
Cualquiera puede ser correcto. Propiedad root con lectura de grupo por el grupo del usuario PHP es un patrón común de hardening. Lo importante es: PHP debe leerlo; atacantes (y usuarios aleatorios) no deben poder escribirlo.
3) ¿Vale la pena cambiar $table_prefix por seguridad?
Es una defensa en profundidad menor, no un escudo. Hazlo si configuras una instalación nueva o consolidas bases de datos. No lo hagas como respuesta de pánico a una posible intrusión; enfócate en parchear, rotar credenciales y contener forensemente.
4) ¿Por qué mi sitio funciona en el front-end pero wp-admin está roto tras cambios de config?
Las rutas de admin son más sensibles a settings de esquema/host/cookie. La mala detección de proxy inverso, problemas de dominio de cookie o reglas de caché que almacenan respuestas de usuarios logueados suelen afectar primero a wp-admin.
5) ¿Cuál es la forma más segura de habilitar debugging en producción brevemente?
Activa logging, no display. Manténlo con límite de tiempo y monitoriza crecimiento de archivos. Prefiere logging a nivel PHP-FPM/servidor web con rotación en lugar de volcar en wp-content/debug.log.
6) Mi “Error establishing a database connection” desaparece si actualizo la página. ¿Qué pasa?
Conexiones intermitentes a la BD suelen significar flaps de red, inestabilidad DNS, máximos de conexiones en BD o autenticación lenta. También puede ser overload de PHP-FPM causando timeouts. Trátalo como un problema de infraestructura hasta demostrar lo contrario.
7) ¿Debo definir WP_HOME y WP_SITEURL en wp-config.php?
Sólo si tienes una razón fuerte (infraestructura inmutable, imágenes de contenedor o cambios frecuentes de entorno). Si las defines, haces que los valores de BD sean irrelevantes—documenta eso, o la próxima persona “arreglará” la BD y nada cambiará.
8) ¿wp-config.php afecta el rendimiento directamente?
Sí, de forma indirecta. El logging de debug, la configuración de object cache, toggles de cron y el manejo de SSL pueden cambiar latencia y carga. Un timeout mal configurado de cache puede convertirse en la dependencia más lenta en cada petición.
9) ¿Cómo evito tumbar el sitio al editar wp-config.php?
Haz backup, lint, prueba bajo el usuario web y despliega por una vía controlada. Y si puedes, usa un nodo canario detrás del balanceador para cambios de configuración.
Pasos siguientes para evitar incidentes repetidos
Si tus outages de WordPress tienen un álbum de grandes éxitos, wp-config.php probablemente sea la pista uno. Las soluciones no son exóticas. Son disciplina.
- Convierte tu “Guion rápido de diagnóstico” en un runbook. Sintaxis, permisos, alcance a BD, corrección de esquema/host, toggles de debug/caché.
- Añade comprobaciones preflight al despliegue. Lint de la config, inclúyela como usuario PHP y corre una probe de conexión a BD usando host y usuario configurados.
- Haz que el logging sea seguro por defecto. No mostrar debug en producción. Rota logs. Alerta por crecimiento de logs y uso de disco.
- Sé conservador con cambios de caché. Valida latencia del backend de cache y fija timeouts que fallen rápido.
- Documenta la URL canónica y comportamiento del proxy. La mayoría de los bucles de redirección son ambigüedad de política fingiendo ser un bug.
Haz eso, y wp-config.php volverá a ser lo que debe ser: un archivo aburrido en el que rara vez pensarás. Aburrido es bueno. Aburrido escala.