
Expires headers Apache es, de las optimizaciones que puedes aplicar a tu servidor si llevas un sitio WordPress, una de las más rentables en relación esfuerzo/resultado. La idea es sencilla: le dices al navegador del visitante cuánto tiempo debe guardar en caché imágenes, CSS, JavaScript y fuentes antes de volver a pedírselos al servidor. El efecto se nota sobre todo en visitas repetidas, que cargan mucho más rápido, pero también reduces consumo de ancho de banda y mejoras métricas como el LCP o el TTFB percibido. En esta guía vamos a ver qué son exactamente estas cabeceras, cómo comprobar si tu sitio las envía ya y cómo configurarlas bien en Apache usando mod_expires y el archivo .htaccess. Y si prefieres que un técnico lo haga por ti, en RedServicio (redservicio.net) puedes encontrar ayuda profesional para cualquier problema de configuración de tu servidor.
Qué son los expires headers y por qué afectan a la velocidad de WordPress
Los expires headers son cabeceras HTTP que tu servidor manda junto con cada recurso. Su función: decirle al navegador cuánto tiempo puede guardar ese archivo en la caché local sin volver a consultar al servidor. Si no las envías, cada vez que un usuario navega entre páginas de tu WordPress, el navegador vuelve a descargar imágenes, hojas de estilo y scripts que seguramente no han cambiado ni un byte. Tiempo tirado a la basura. Y ancho de banda también.
Piénsalo con un ejemplo. Un visitante llega a tu portada y descarga el logo, la hoja de estilos del tema, jQuery y varias fuentes. Si luego navega a una entrada del blog, todos esos recursos se reutilizan desde la caché en lugar de descargarse otra vez. En un WordPress típico esto puede ahorrar entre el 60 y el 90 por ciento del peso de la página en visitas repetidas. No está nada mal para unas pocas líneas de configuración.
La lógica general se basa en el tipo de contenido:
- Imágenes (JPG, PNG, WebP, SVG): periodos largos, de 6 meses a 1 año, porque rara vez cambian.
- Fuentes y archivos de iconos: también periodos largos, de 6 meses a 1 año.
- CSS y JavaScript: periodos intermedios, de 1 semana a 1 mes, o largos si tu tema o plugins usan versionado en las URLs.
- HTML y contenido dinámico: sin caché o con periodos muy cortos, para que las actualizaciones se vean de inmediato.
Cómo comprobar si tu sitio ya envía expires headers
Antes de tocar nada, mejor verificar el estado actual. Tienes varias formas rápidas:
- Con las herramientas de desarrollador del navegador: abre tu web, pulsa F12, ve a la pestaña Network (Red), recarga la página y haz clic en cualquier imagen o archivo CSS. En la sección Response Headers busca Cache-Control o Expires. Si no aparecen, tu servidor no las está enviando.
- Con cURL desde la terminal: ejecuta el siguiente comando sustituyendo la URL por la de un recurso real de tu sitio:
Con esta instrucción verás las cabeceras de respuesta: curl -I https://tudominio.com/wp-content/uploads/2024/01/imagen.jpg. Busca líneas como Cache-Control: max-age=31536000 o Expires: fecha futura.
Un detalle que conviene no pasar por alto: si tu sitio pasa por un CDN como Cloudflare, puede que algunas cabeceras las añada el propio CDN y no tu servidor Apache. Si puedes, comprueba también un recurso directamente en el origen.
Cómo configurar expires headers en Apache con mod_expires y .htaccess
Lo habitual es definir los expires headers Apache mediante el módulo mod_expires, que viene activo por defecto en la mayoría de hostings compartidos y VPS con Apache. Primero confirma que el módulo está habilitado. En un VPS propio puedes comprobarlo con:
apachectl -M | grep expires
Si no aparece, actívalo en sistemas Debian/Ubuntu con sudo a2enmod expires y reinicia Apache con sudo systemctl restart apache2. En hostings compartidos normalmente ya viene activado; si tienes dudas, pregunta a tu proveedor o al equipo de RedServicio.
Confirmado el módulo, toca editar el archivo .htaccess que está en la raíz de tu WordPress (por FTP o desde el administrador de archivos de tu panel). Añade este bloque, preferiblemente antes del bloque de reglas de WordPress:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType text/html "access plus 0 seconds"
</IfModule>
Consejo: haz siempre una copia de seguridad del .htaccess antes de editarlo. Un error de sintaxis aquí puede dejar tu web caída con un error 500. Me ha pasado a clientes que editaban «rápido» desde el panel; no es plan.
Guarda los cambios, vacía la caché de tu plugin de caché si usas alguno (WP Rocket, W3 Total Cache, LiteSpeed Cache) y vuelve a comprobar con cURL o las herramientas de desarrollador. Deberías ver la cabecera Cache-Control: max-age con los valores que has definido. Si aparece, la configuración funciona.
Reglas de caché recomendadas por tipo de archivo en WordPress
No todos los archivos van a cachearse igual. Ahí está buena parte del trabajo: distinguir entre lo que cambia cada semana y lo que, en la práctica, no se mueve ni un ápice. Estas son las pautas que aplicamos de forma estándar en nuestros proyectos WordPress:
- Imágenes (jpg, png, webp, gif, svg, ico): 1 año (31536000 segundos). Una imagen apenas cambia; si actualizas una, renómbrala o cambia su versión en la URL.
- Fuentes (woff, woff2, ttf, eot): 1 año. Lo mismo que las imágenes: rara vez se tocan.
- CSS y JavaScript: entre 1 mes y 1 año, según cómo trabajes. Si tu tema o plugins versionan los archivos (style.css?ver=1.2), puedes apretar con periodos largos sin sustos.
- PDF y documentos descargables: 1 semana o 1 mes, porque suelen actualizarse más de lo que parece.
- HTML y documentos dinámicos: nada de cachearlos en el navegador. Como máximo, expires de 0 con must-revalidate, para que el contenido refleje siempre los cambios.
Una configuración en .htaccess que cubre estos casos quedaría así:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg «access plus 1 year»
ExpiresByType image/png «access plus 1 year»
ExpiresByType image/webp «access plus 1 year»
ExpiresByType font/woff2 «access plus 1 year»
ExpiresByType text/css «access plus 1 month»
ExpiresByType application/javascript «access plus 1 month»
ExpiresByType text/html «access plus 0 seconds»
</IfModule>
Adapta los MIME types a tu servidor. Ojo con esto: hay servidores que sirven JavaScript como application/x-javascript o text/javascript, así que si tienes dudas, define los tres.
Errores comunes al configurar expires headers y cómo solucionarlos
Tras años tocando Apache, estos son los fallos que más veces hemos visto (y arreglado):
El módulo mod_expires no está activo
Si las cabeceras no aparecen por ningún lado, ejecuta a2enmod expires y reinicia Apache con systemctl restart apache2. En hosting compartido no tendrás esa posibilidad; toca escribir al proveedor si la directiva no surte efecto.
Error 500 tras editar .htaccess
Casi siempre es una directiva mal escrita o fuera de contexto. Revisa la sintaxis comilla por comilla y comprueba que las reglas están dentro de los bloques IfModule. ¿Persiste el error? Recupera la copia de seguridad que hiciste antes de editar. Hiciste copia, ¿verdad?
Conflicto con el plugin de caché
WP Rocket o W3 Total Cache generan sus propias cabeceras. Si duplicas la configuración, acabas con valores contradictorios y el navegador no sabe a qué atenerse. Decide quién manda: la caché del navegador la gestiona el plugin o la gestiona Apache. Pero no los dos a la vez con valores distintos.
Los cambios no se reflejan
Lo más probable: tu navegador sigue devolviéndote la caché vieja. Prueba siempre en modo incógnito o con una carga forzada (Ctrl+F5), y vacía cualquier CDN como Cloudflare antes de dar la configuración por fallida.
Cachear HTML por error
El error más peligroso con diferencia. Si expires headers largos se aplican a text/html, tus visitantes verán versiones desactualizadas de la web aunque tú publiques cambios cada día. Verifica con curl que la cabecera de las páginas HTML es max-age=0 o no-cache.
Con paciencia y algo de método, cualquiera de estos problemas se resuelve en minutos: revisa la sintaxis, el estado de los módulos y las cabeceras reales que devuelve el servidor. Nada más.
Conclusión: verifica y mide la mejora tras aplicar los expires headers
Pocas optimizaciones dan tanto por tan poco como configurar expires headers en Apache. Unas líneas en .htaccess y, de golpe, las peticiones repetidas se desploman y las visitas recurrentes van bastante más rápido.
Eso sí: el trabajo no acaba al guardar el archivo. Comprueba con curl -I o con las herramientas de desarrollador del navegador que las cabeceras llegan como deben, y mide el impacto real con PageSpeed Insights, GTmetrix o WebPageTest. Fíjate sobre todo en la métrica «Serve static assets with an efficient cache policy»: si desaparece de tus auditorías, la configuración ha hecho su trabajo.
Los expires headers, eso sí, son solo una pieza del rendimiento. Combínalos con compresión, minificación y un plugin de caché para sacarle todo el partido. Y si tu sitio lanza errores 500, las cabeceras no responden como esperabas o prefieres directamente que un profesional revise el servidor entero, en RedServicio (redservicio.net) podemos ayudarte a optimizar tu WordPress y tu hosting de principio a fin.

