Oleksii Siniaiev
RUUKESEN
Navegación de la página del artículo

Artículo del blog Artículos 11 min de lectura

CSS funciona en local pero se rompe en producción: cómo LiteSpeed UCSS elimina tus estilos

Una historia real de depuración: el tema funcionaba en local pero se rompió en producción porque LiteSpeed UCSS eliminó reglas CSS. Cómo comparar lo que recibe el navegador, los cuatro bugs y cómo prevenirlo.

Дебаг продакшен-CSS: порівняння байтів таблиці стилів, яку завантажує браузер, із задеплоєним вихідником
En esta página

La compilación fue limpia. El tema se veía bien en mi portátil en todos los breakpoints. Hice el deploy, abrí el sitio en vivo en mi teléfono y el diseño estaba roto: la navegación de escritorio aparecía en móvil, una lista de habilidades con estilo se renderizaba como viñetas planas, el contenido se salía del borde derecho de la pantalla y algunos colores de texto fallaban el contraste. Nada de eso pasaba en local. El código que desplegué era byte por byte el mismo que había probado.

Esta es la brecha que cuesta horas: el navegador no renderiza el CSS que escribiste. Renderiza el CSS que tu hosting decidió servir. En un stack LiteSpeed con la optimización de CSS no usado activada, esos dos no son el mismo archivo. Este artículo recorre cómo lo descubrí, los cuatro bugs que causó y el método repetible para depurar CSS en producción cuando “funciona en mi máquina” deja de ser suficiente.

Respuesta rápida: por qué el CSS funciona en local pero se rompe en producción

Si tu CSS funciona en local pero se rompe en producción, la causa no suele estar en tu código. Es una capa de caché u optimización entre tu servidor y el navegador que reescribe, elimina o reordena tu hoja de estilos. LiteSpeed Cache con las funciones UCSS (CSS no usado) y CCSS (CSS crítico) es un culpable común: escanea una página, decide qué reglas “no se usan” y sirve una hoja de estilos recortada. Las reglas ligadas a clases añadidas después, a estados dinámicos o a páginas que el escáner nunca visitó se eliminan. La solución es comparar lo que el navegador descarga realmente con lo que desplegaste, y luego excluir los selectores rotos de la optimización o desactivar la función.

El contexto: el entorno local y producción no eran lo mismo

Mi entorno local corre WordPress mediante Docker sin capa de caché. Producción corre en Hostinger con LiteSpeed y LiteSpeed Cache activado, incluyendo caché de páginas, minificación de CSS, combinación de CSS y generación de CSS no usado. En local, el navegador pide style.css y obtiene exactamente el archivo en disco. En producción, el navegador pide una hoja de estilos optimizada y generada por máquina que LiteSpeed construyó adivinando qué reglas necesita la página.

Esa diferencia es invisible hasta que la buscas. El HTML era idéntico. El deploy fue un git pull limpio. Y el resultado renderizado estaba mal. El primer instinto es culpar a tus propias media queries o a un bug de especificidad. El movimiento más rápido es confirmar si el navegador siquiera recibió la regla que estás depurando.

El diagnóstico que salvó el día: compara bytes, no comportamiento

El avance fue aburrido y cuantitativo. Abrí DevTools en ambos entornos, fui a la pestaña Network y miré la carga real de CSS.

  • Local: la hoja de estilos pesaba 53 378 bytes.
  • Producción: la hoja de estilos que descargaba el navegador pesaba 38 886 bytes.

Casi 15 KB de CSS faltaban en producción. Esa única comparación reformuló todo el problema. No estaba persiguiendo un bug de maquetación. Estaba persiguiendo una hoja de estilos que el hosting había editado antes de que llegara al navegador. Contar las reglas parseadas lo confirmó: el navegador había cargado unas 250 de las 311 reglas de mi archivo fuente. Las otras 61 habían desaparecido.

Puedes hacer esa comprobación tú mismo en la consola de DevTools:

// Contar las reglas CSS que el navegador realmente parseó
[...document.styleSheets]
  .map(s => { try { return s.cssRules.length } catch (e) { return 0 } })
  .reduce((a, b) => a + b, 0);

Compara el número en local y en producción. Si producción es menor, algo está eliminando tu CSS. Abre la hoja de estilos servida directamente (abre la URL del CSS desde la pestaña Network) y busca un selector que sepas que está roto. Si no está en el archivo, el problema está aguas arriba de tu código.

Los cuatro bugs y qué fallaba en realidad

Bug 1: la navegación de escritorio aparecía en móvil

El botón del menú móvil había desaparecido y la navegación de escritorio completa era visible en una pantalla estrecha. Mi primera sospecha fue una media query rota. No lo era. El bloque de media query que ocultaba la navegación de escritorio por debajo de un breakpoint había sido eliminado por completo de la hoja de estilos servida. UCSS decidió que esas reglas no se usaban porque el escáner evaluó la página en un viewport de escritorio, donde la navegación de escritorio es visible y el botón móvil está oculto. Desde esa única instantánea, las reglas móviles parecían muertas. No lo estaban. Solo aplicaban en un viewport que el escáner nunca probó.

Bug 2: una lista con estilo se renderizaba como viñetas planas

Una lista de habilidades que debía renderizarse como chips con estilo salió como una lista de viñetas por defecto. Misma causa raíz, distinto disparador. Las clases que daban estilo a la lista estaban en el HTML, pero las reglas que las apuntaban estaban entre las 61 que se eliminaron. La detección de CSS no usado es frágil con contenido condicional, paginado o que solo se renderiza en ciertos estados. Si el escáner no ve la clase en el contexto exacto que espera, trata la regla como eliminable.

Bug 3: el contenido se desbordaba del viewport en móvil

En móvil, los bloques se salían del borde derecho y producían una barra de scroll horizontal. Este no fue LiteSpeed. Fue el editor: Gutenberg había escrito un width inline en un bloque, y un estilo inline gana sobre una regla de la hoja de estilos. Con la hoja ya recortada, no quedaba nada que limitara el elemento. La solución fue CSS defensivo que no depende de que la optimización sobreviva:

.entry-content img,
.entry-content figure,
.entry-content .wp-block-image {
  max-width: 100%;
  height: auto;
}

.entry-content {
  overflow-x: hidden;
}

Reglas base defensivas como max-width: 100% y un overflow-x: hidden en el contenedor son un seguro barato. Protegen el diseño incluso cuando falta una regla más específica o se cuela un estilo inline.

Bug 4: los colores de texto fallaban el contraste WCAG

Algunos colores de texto apagados y estados de enlace no cumplían el contraste WCAG AA contra su fondo. Esto fue un problema real de código, no de caché, pero producción es donde se hizo visible porque los estilos vecinos se habían desplazado. Ajusté los tokens de color hasta que el texto del cuerpo y los estados interactivos superaron la proporción de 4.5:1 para texto normal. El contraste es una de las victorias de accesibilidad más fáciles de verificar: tanto el selector de color de DevTools como Lighthouse marcan los fallos directamente.

La causa raíz: cómo falla la optimización de CSS “no usado”

LiteSpeed UCSS y CCSS existen por una buena razón. Enviar menos CSS mejora el Largest Contentful Paint y reduce los recursos que bloquean el renderizado. La función carga una página, observa qué reglas aplican y genera una hoja de estilos más ligera. El problema está en la palabra “aplican”. Una regla aplica solo en el contexto que observó el generador:

  • Las reglas específicas de viewport tras media queries parecen no usadas si la página se escanea en un solo tamaño de pantalla.
  • Las reglas específicas de estado para hover, focus, menús abiertos o acordeones expandidos parecen no usadas si el estado nunca se dispara durante el escaneo.
  • Las clases dinámicas añadidas por JavaScript tras la carga son invisibles para un escaneo estático de una sola pasada.
  • Las reglas específicas de plantilla para páginas que el generador nunca visitó simplemente nunca se contabilizan.

El resultado es una hoja de estilos correcta para una instantánea congelada de una página e incorrecta para cada variación que la instantánea no capturó. Los diseños móviles, los estados interactivos y el contenido condicional son justo los casos que se rompen.

Un método repetible para depurar CSS en producción

La lección se generaliza mucho más allá de LiteSpeed. Siempre que algo se renderiza bien en local pero se rompe en vivo, verifica los bytes antes de tocar el código.

  1. Confirma la entrada. Abre la URL de la hoja de estilos en vivo desde la pestaña Network y léela. No asumas que el navegador tiene el archivo que desplegaste.
  2. Compara tamaños. Una gran diferencia en la carga de CSS entre local y producción es una señal fuerte de que una capa de optimización reescribe tu salida.
  3. Busca el selector roto. Si la regla que depuras no está en el archivo servido, el bug está en el pipeline de entrega, no en tu fuente.
  4. Reproduce con las optimizaciones activadas. Activa la caché y la optimización de CSS en un entorno de staging para que el fallo aparezca antes de que lo vean los usuarios.
  5. Arregla en la capa correcta. Excluye los selectores afectados de la optimización, o desactiva la función, en lugar de reescribir CSS que funciona para esquivar una regla eliminada.

Cómo prevenirlo en un stack LiteSpeed o CDN

Una vez que conoces el modo de fallo, la prevención es sencilla.

RiesgoQué hacer
UCSS elimina reglas de media query y de estadoAñade los selectores críticos a la lista de permitidos de UCSS, o desactiva UCSS para plantillas con CSS responsive o interactivo pesado.
Las clases dinámicas se eliminanExcluye de la optimización las clases que conmuta JavaScript, o renderízalas en el servidor para que el escáner las vea.
Los estilos inline anulan el CSS recortadoEnvía reglas base defensivas (max-width: 100%, overflow-x: hidden en el contenedor) que aguanten sin la hoja completa.
Los bugs solo aparecen en producciónReplica la caché de producción en staging y prueba los estados móviles e interactivos con la optimización activada.
CSS optimizado obsoleto tras un deployPurga la caché de CSS y de páginas como parte del release, luego vuelve a comprobar la carga servida.

Tras cada deploy que toca CSS, ahora purgo la caché de LiteSpeed y compruebo de inmediato el tamaño de la hoja servida contra el local. Tarda treinta segundos y atrapa justo esta clase de bug antes de que lo vea nadie más.

Preguntas frecuentes

¿Por qué mi CSS funciona en local pero no en producción?

La mayoría de las veces porque producción está detrás de una capa de caché u optimización que reescribe tu hoja de estilos. La minificación, la combinación de CSS y la eliminación de CSS no usado pueden quitar reglas que tus páginas sí necesitan. Compara el CSS que el navegador descarga en cada entorno antes de asumir que el bug está en tu código.

¿Qué es LiteSpeed UCSS y por qué elimina CSS?

UCSS (CSS no usado) es una función de LiteSpeed Cache que genera una hoja de estilos recortada con solo las reglas que cree que una página usa. Mejora el rendimiento de carga, pero puede clasificar mal reglas que solo aplican en ciertos viewports, en ciertos estados o a clases añadidas dinámicamente, y eliminarlas.

¿Cómo sé si mi hosting está eliminando CSS?

Abre DevTools, ve a la pestaña Network y compara el tamaño de la hoja servida en producción con tu archivo local. Luego abre la URL del CSS de producción y busca un selector que sepas que está roto. Si falta, la capa de entrega lo quitó.

¿Debería simplemente desactivar la optimización de CSS?

No necesariamente. El beneficio de rendimiento es real. Empieza excluyendo los selectores o plantillas concretos que se rompen, y desactiva la función por completo solo si las exclusiones no bastan. Mantén la optimización donde es segura.

¿Cómo pruebo esto antes de desplegar?

Ejecuta un entorno de staging con la misma configuración de caché y optimización que producción, y prueba allí los breakpoints móviles y los estados interactivos. Los bugs que dependen de la optimización no aparecerán en una configuración local sin optimizar.

Puntos clave

  1. Cuando el CSS funciona en local pero se rompe en producción, verifica los bytes que recibe el navegador antes de depurar tu código.
  2. LiteSpeed UCSS y optimizadores similares pueden eliminar reglas ligadas a media queries, estados interactivos y clases dinámicas.
  3. Una carga de CSS menor en producción que en local es una señal fuerte de que tu hoja de estilos está siendo reescrita.
  4. Envía reglas base defensivas para que un selector ausente o un estilo inline no destruyan el diseño.
  5. Replica la caché de producción en staging, y purga la caché y vuelve a comprobar el CSS servido tras cada deploy.

Artículos relacionados

Compartir este artículo

LinkedIn X Email

Explorar más

May 30, 2026

Configuración de Claude Code: guía para principiantes

Guía paso a paso para instalar Claude Code, configurar CLAUDE.md, entender los permisos y…

May 20, 2026

Claude Code Subagentes: agentes listos para flujos más seguros y baratos

Guía práctica 2026 de subagentes Claude Code con ejemplos .claude/agents listos para explorer, planner,…

May 17, 2026

Agentes de IA en el flujo de trabajo del desarrollador: guía práctica

Guía práctica actualizada a mayo de 2026 sobre agentes de IA en desarrollo: Claude…