Caso real: colchoneriasgonzalez.com/blog/ — blog WordPress de Colchonerías González, en el mismo vhost Plesk/nginx que la tienda PrestaShop. Núcleo WordPress 7.1, PHP 8.3.
El cliente entra al dashboard y parece que “todo está destrozado”: fondo negro, enlaces azules, menú en vertical. Es un WordPress admin sin CSS. El front del blog se veía bien. El login también. El lío estaba después de iniciar sesión: el HTML del panel se pintaba, los estilos no llegaban al navegador.
No reinstalé WordPress. No toqué nginx. No desactivé plugins “a ver si es este”. Medí las URLs de CSS, encontré un 403 y lo dejé estable con un define en wp-config.php — y con una lección de PHP que casi tumba el blog entero.
Qué se veía: WordPress admin sin CSS (el login sí se veía bien)
El contraste era el dato útil. Si el login carga con estilos y el dashboard no, el theme del front no es el sospechoso. Tampoco “WordPress se ha roto entero”.
- El front de
/blog/se veía bien. - El login (
/blog/wp-login.php) también: tenía CSS. - Tras entrar,
/blog/wp-admin/era HTML crudo: fondo negro, enlaces azules, menú en vertical. - Había unos 1.010 comentarios en moderación (spam). Eso empeoraba la sensación de caos. No era la causa del CSS.
El wp-admin sin estilos no era un theme destrozado ni un plugin “raro”. El panel de administración WordPress generaba HTML; el CSS del admin no llegaba al navegador.
Diagnóstico: load-styles.php 403 en la URL concatenada
WordPress no sirve el CSS del admin como un archivo estático único. Lo junta en wp-admin/load-styles.php con una query string de handles. Medí esas peticiones, no “el aspecto”.
La URL corta de login a wp-admin/load-styles.php (pocos handles: dashicons, buttons, forms, login) devolvía HTTP 200 y CSS real (~111 KB).
Los CSS sueltos también iban bien:
wp-admin/css/common.min.css→ 200wp-admin/css/dashboard.min.css→ 200wp-admin/css/dashicons.min.css→ 200
La URL concatenada del dashboard (más handles: admin-bar, common, dashboard, wp-components, wp-commands, wp-preferences, etc., ver=7.1) devolvía HTTP 403 Forbidden.
| Petición | HTTP | Qué devolvía |
|---|---|---|
load-styles.php corto (login: dashicons, buttons, forms, login) | 200 | CSS real, ~111 KB |
common.min.css, dashboard.min.css, dashicons.min.css | 200 | Hojas sueltas OK |
load-styles.php concatenado del dashboard (ver=7.1) | 403 | Forbidden: el CSS no llega |
Eso cierra el caso: el navegador pedía una hoja enorme, el servidor la cortaba, y el HTML se quedaba desnudo. Por eso el login sobrevivía (pide poco CSS) y el dashboard no.
Causa real: concatenación + WAF en nginx/Plesk
WordPress 7.1 concatena más hojas que versiones viejas. La query string de load-styles.php es más larga. En este hosting, nginx / ModSecurity / WAF de Plesk corta esa petición.
La causa no era “el admin está corrupto”. Era esta URL:
wp-admin/load-styles.php?...load[chunk_0]=lista,enorme
WordPress junta todo el CSS del admin en una sola petición. El WAF la bloquea. El HTML del panel se pinta sin estilos.
Lo que no hice, a propósito:
- No toqué nginx.
- No reinstalé WordPress.
- No desactivé plugins para “probar”.
No hacía falta. Las URLs cortas ya daban 200. El problema era la URL larga.
Cómo se arregló (sin tocar nginx)
Backup de blog/wp-config.php. Luego, antes del comentario /* That's all, stop editing! Happy publishing. */ — nunca dentro —:
define('CONCATENATE_SCRIPTS', false);
Eso hace que el admin cargue cada CSS/JS por separado. Esas URLs cortas ya daban 200. El WAF sigue ahí; WordPress ya no le manda la URL concatenada.
Un define. Sin tocar el core. Sin pelear con el WAF de Plesk.
El error 500 wp-config al insertar dentro del comentario
El primer intento salió mal. El texto That's all, stop editing está dentro de un comentario PHP /* ... */. Si insertas el define (o pegas otro bloque que trae un */) ahí dentro, cierras el comentario en ese */.
PHP no anida comentarios. El resto del archivo — el cierre That's all, stop editing! Happy publishing. */ y el require de wp-settings.php — queda como código suelto. Resultado: error 500 en /blog/ y en /blog/wp-admin/.
Es fácil de hacer si pegas un snippet “completo” (con su propio /* ... */) en mitad del comentario de WordPress. El primer */ que aparece cierra el bloque. Lo que queda debajo deja de ser comentario y PHP intenta ejecutarlo.
La tienda PrestaShop (/) siguió en 200. El vhost es compartido; el fallo era solo del wp-config.php del blog.
Qué hice entonces:
- Restaurar el backup de
wp-config.php. php -l→ OK. Blog otra vez 200.- Volver a insertar el
defineencima del comentario, no dentro. php -l→ OK. Blog 200./wp-admin/respondía 302 al login (normal).- Hard refresh (Ctrl+F5). El panel volvió con CSS.
La lección no es menor: el define es la solución correcta; meterlo dentro del comentario de “That’s all” tumba el blog entero. Siempre backup + php -l + recargar el front, no solo el admin.
Resultado y prevención
- Front del blog en 200.
- Login y dashboard con estilos.
- La concatenación de scripts/CSS del admin queda desactivada, permanente y segura en este hosting.
- No se reinstaló WordPress. No se cambió el WAF. Workaround limpio en
wp-config.php.
El 403 de nginx/WAF sigue existiendo para una URL concatenada larga. Lo que cambió es que el panel de administración WordPress ya no la pide.
Dónde poner el define:
- En
wp-config.php, antes de/* That's all, stop editing! Happy publishing. */. - Nunca dentro de ese comentario.
- Nunca después del
requiredewp-settings.php(ahí ya es tarde).
Prevención realista:
- Backup de
wp-config.phpantes de tocarlo. - Validar con
php -l. - Recargar el front del blog, no solo el admin.
- Si el login tiene CSS y el dashboard no, mira
load-styles.php(200 vs 403) antes de reinstalar o desactivar plugins a ciegas. - No toques el core de WordPress para “arreglar estilos”.
Preguntas frecuentes
¿Es un virus?
No. En este caso el CSS del admin no llegaba al navegador porque el WAF cortaba la URL concatenada. El HTML se pintaba. Un theme “roto” o un plugin suelto habrían tenido otro rastro. Aquí las hojas sueltas y la URL corta de login daban 200.
¿Por qué el login sí tiene CSS?
Porque pide pocos handles. La URL de load-styles.php es corta y pasa el WAF. El dashboard de WordPress 7.1 pide muchas más hojas en una sola query: esa es la que choca con nginx / ModSecurity / Plesk.
¿Es seguro dejar CONCATENATE_SCRIPTS en false?
En este hosting, sí: permanente y suficiente. El admin carga más peticiones cortas en lugar de una concatenada enorme. No es un parche sucio ni un cambio de core. Es una constante oficial de WordPress.
¿Hay que tocar nginx?
No en este caso. No se tocó nginx ni se relajó el WAF. El arreglo está en wp-config.php: WordPress deja de generar la URL larga. Si alguien abre a mano la concatenada, el 403 puede seguir ahí. Eso no impide usar el admin.
¿Un error 500 wp-config puede tumbar solo el blog y no la tienda?
Sí, si el blog WordPress y la tienda PrestaShop comparten vhost pero no el mismo wp-config.php. Aquí /blog/ y /blog/wp-admin/ cayeron a 500; la tienda en / siguió en 200. Restaurar el backup devolvió el blog.
Conclusión
En Colchonerías González el dashboard “destrozado” no era un theme ni mil comentarios de spam: era el CSS del admin bloqueado en una URL concatenada. El login se salvaba porque pedía poco. WordPress 7.1 pide más. El WAF de Plesk corta lo largo.
La solución correcta no fue reinstalar ni pelear con nginx. Fue diagnosticar 200 vs 403, poner el define encima del comentario de “That’s all”, y no volver a meter PHP dentro de un /* ... */.
Si tu wp-admin salió sin estilos o te ha dado un 500 al editar wp-config, puedo hacer el mismo tipo de arreglo: diagnóstico, backup y un cambio limpio. Mantenimiento WordPress y soporte PrestaShop — Madrid y remoto en toda España.
Mantenimiento WordPress · Soporte técnico PrestaShop · Contactar
