Lo hice y lo entendí

El blog de Vicente Navarro
02 dic

Comprimir y cachear las páginas generadas por WordPress

Las dos entradas anteriores:

trataban de conseguir optimizar el ancho de banda de nuestro servidor web comprimiendo el documento para que ocupara menos y de cómo lograr que el mismo contenido no se tenga que comprimir una y otra vez malgastando inútilmente ciclos de CPU. Sin embargo, aquella teoría estaba orientada a páginas estáticas… ¿Cómo podemos hacer lo mismo para contenido creado dinámicamente como es el generado por WordPress? ¿Es aplicable?

El contenido generado por WordPress se puede enviar comprimido a través de varios mecanismos. Por un lado, podemos simplemente señalar la opción “WordPress debería comprimir las entradas (gzip) si los navegadores lo requieren” del panel de “Opciones de lectura” de WordPress. Por otro, podemos habilitar la opción zlib.output_compression del php.ini (en /etc/php5/apache2/php.ini en Debian):

; Transparent output compression using the zlib library
; Valid values for this option are 'off', 'on', or a specific buffer size
; to be used for compression (default is 4KB)
; Note: Resulting chunk size may vary due to nature of compression. PHP
;       outputs chunks that are few hundreds bytes each as a result of
;       compression. If you prefer a larger chunk size for better
;       performance, enable output_buffering in addition.
; Note: You need to use zlib.output_handler instead of the standard
;       output_handler, or otherwise the output will be corrupted.
zlib.output_compression = On

Y por supuesto, podemos usar el mod_deflate, que nos comprime la salida generada por WordPress sin problemas si lo tenemos configurado para que nos comprima los ficheros text/html:

AddOutputFilterByType DEFLATE text/html

Es recomendable no usar más de un método de compresión porque pueden chocar entre ellos.

Sin embargo, todos estos métodos necesitan estar comprimiendo una y otra vez las mismas páginas. ¿Podríamos usar el mod_cache con alguno de estos métodos de compresión como hacíamos con contenidos estáticos y el mod_deflate? Pues desafortunadamente la respuesta es un no.

Si tomamos una traza de red para ver qué cabeceras devuelve una petición enviada a un servidor de WordPress:

HTTP/1.1 200 OK
Date: Sun, 02 Dec 2007 10:05:43 GMT
Server: Apache/2.2.3 (Debian) PHP/5.2.0-8+etch7
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Last-Modified: Sun, 02 Dec 2007 10:05:44 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Pragma: no-cache
Keep-Alive: timeout=15, max=97
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

vemos que entre otras cosas, una respuesta con “Cache-Control: no-cache” no es cacheada por defecto en ningún caso, tal y como leemos en la Apache HTTP Server Version 2.2: Caching Guide:

Likewise, if the response includes the “no-store” option in a “Cache-Control:” header, it will not be stored unless the CacheStoreNoStore has been used.

Sin embargo, el contenido de WordPress, aunque se genera dinámicamente, es bastante estático. Una entrada se escribe y a menos que se generen nuevos comentarios o el autor corrija algo, va a permanecer inalterable durante mucho tiempo. Debería de ser fácilmente cacheable… y en realidad así lo es gracias a distintos plugins.

WP-Cache

El WP-Cache es un plugin para WordPress escrito por Ricardo Galli. Cada vez que una página es generada por WordPress, este plugin la almacena como un fichero HTML estático en el directorio de WordPress wp-content/cache y las siguientes veces que se pida, se servirá dicho fichero en lugar de volver a generar la página con peticiones a la base de datos. Si se añade algún comentario o se edita la entrada, el plugin se da cuenta y vuelve a crear la entrada estática de la caché. También se encarga de eliminar entradas viejas de la caché cuando expiran.

El WP-Cache es el plugin de caché para WordPress más conocido, y la verdad es que funciona muy bien y descarga mucho al servidor web evitándole enviar una y otra vez las mismas peticiones a la base de datos. Sin embargo, tiene una importante carencia: Si la compresión está habilitada de alguna forma (no con la opción “WordPress debería comprimir las entradas (gzip) si los navegadores lo requieren” del panel de control de WordPress, ya que no funciona bien con ella, pero sí con el “zlib.output_compression” o con el mod_deflate), la página servida no es almacenada de forma comprimida, teniendo el servidor que comprimir las mismas páginas de la caché una y otra vez si queremos servir páginas comprimidas.

Buscando por la web, podemos encontrar formas de modificar el WP-Cache para conseguir la tan deseada combinación de cacheo+compresión:

pero no nos hace falta. Ya han salido plugins basados en el WP-Cache que lo mejoran en este aspecto.

1 Blog Cacher

El 1 Blog Cacher 2.0, de Javier García proporciona importantes mejoras sobre el WP-Cache, incluyendo la posibilidad de dejar el contenido cacheado comprimido. Sin embargo, en la versión actual tiene el problema de que si tienes una “Dirección de WordPress (URL)” diferente de la “Dirección del blog (URL)”, tal y como podemos leer en Giving WordPress its Own Directory While Leaving Your Blog in the Root Directory, las páginas se cachean pero luego no se sirven de la caché. Y es por este motivo que yo no puedo usar este útil plugin, aunque Javier ya ha expresado su intención de corregir el problema en la siguiente versión.

WP Super Cache

El WP Super Cache es otro excelente plugin enfocado a que una página pueda resistir una avalancha temporal de visitas sin problemas. La idea es que el WP-Cache, aunque es un excelente producto, aún necesita arrancar todo el motor de PHP y acceder a la base de datos para comprobar si la entrada ha cambiado. En cambio, el WP Super Caché crea una estructura de ficheros y directorios estática en wp-content/cache/supercache réplica exacta de la que las URLs con fancy permalinks (que son requeridos) de WordPress nos muestran.

El WP Super Cache sólo sirve páginas generadas por él a usuarios que no hayan dejado ningún comentario (y por ello no tengan cookie del sitio, claro) y a los que no estén registrados. A los demás no les puede servir páginas suyas porque es posible que tengan que ver páginas con los nuevos comentarios que han dejado o ver páginas que han editado. A estos usuarios (que serán una minoría) se les sirve una página cacheada por WP-Cache, ya que el plugin WP Super Cache mantiene al mismo tiempo su propia caché y la del WP-Cache, de forma que tiene ambos mecanismos en funcionamiento en todo momento. Pero no sólo eso; el WP Super Caché puede añadir compresión a las páginas cacheadas por la parte de WP-Cache y a las cacheadas por él, obteniendo la funcionalidad que necesitábamos.

Así, con unas sencillas RewriteRule que crea el WP Super Cache en el fichero .htaccess del directorio raíz del blog, y que comprueban las cookies del usuario y si la página está cacheada, el Apache nos sirve dichas páginas sin acceder ni a un fichero PHP y sin ni un sólo acceso a la base de datos.

Las reglas son las siguientes, y comprobamos que se verifica que no haya cookies, que la petición no sea una query, y se mira si se puede enviar la versión comprimida o no (HTTP:Accept-Encoding):

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} !.*s=.*
RewriteCond %{HTTP_COOKIE} !^.*comment_author_.*$
RewriteCond %{HTTP_COOKIE} !^.*wordpressuser.*$
RewriteCond %{HTTP_COOKIE} !^.*wp-postpass_.*$
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1index.html.gz -f
RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1index.html.gz [L]

RewriteCond %{QUERY_STRING} !.*s=.*
RewriteCond %{HTTP_COOKIE} !^.*comment_author_.*$
RewriteCond %{HTTP_COOKIE} !^.*wordpressuser.*$
RewriteCond %{HTTP_COOKIE} !^.*wp-postpass_.*$
RewriteCond %{DOCUMENT_ROOT}/wp-content/cache/supercache/%{HTTP_HOST}/$1index.html -f
RewriteRule ^(.*) /wp-content/cache/supercache/%{HTTP_HOST}/$1index.html [L]
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

</IfModule>
# END WordPress

Sin embargo, estas reglas, que son las que crea el WP Super Cache 0.5.1, no funcionan si el wordpress no está instalado en el directorio raíz del blog, como se puede verificar fácilmente estudiándolas un poco. Es decir, si tienes un blog en http://www.example.com/ y el WordPress instalado ahí también, funcionan, pero si tienes ambas cosas en http://www.example.com/blog/, no funcionará, así como si la URL del blog es distinta de la de WordPress, como pasaba también con el 1 Blog Cacher.

Podemos modificarlas para adaptarlas a nuestro entorno, pero el plugin las volverá a modificar cuando abramos su panel de control. O también podemos dejar que las modifique en el .htaccess y poner las reglas modificadas en el fichero de configuración del servidor virtual junto con la directiva “AllowOverride None” para que Apache no use los ficheros .htaccess.

Pero quizás lo más sencillo, y por lo que me he decidido yo, es usar el WP-Cache modificado por WP Super Cache que almacena los documentos comprimidos y no usar su caché propia. Es una opción que permite el panel de configuración del plugin. Además, no hemos de olvidar habilitar la compresión, que para eso estamos usando este plugin.

Y así es como lo tengo configurado yo:

WP Super Cache

Por cierto, en una página servida por WP-Cache podremos ver al final de su código fuente algo como esto:

<!-- Dynamic Page Served (once) in 3.313 seconds -->
<!-- Cached page served by WP-Cache -->
<!-- Compression = gzip -->

Mientras que si ha sido servida por el WP Super Cache veremos algo como:

<!-- Dynamic Page Served (once) in 1.087 seconds -->
<!-- super cache gz -->

No olvidemos comprimir los ficheros CSS y los Javascript

Con el WP Super Cache resolvemos el problema de “cachear + comprimir + minimizar los accesos a la base de datos” para todo el contenido generado por WordPress. Sin embargo, aún tenemos algunos ficheros estáticos de uso muy frecuente (y estoy pensando en concreto en los ficheros CSS y Javascript de la página) que podemos querer comprimir y cachear aprovechando lo que hemos aprendido del mod_cache y del mod_deflate.

Para ello, una configuración como la siguiente en la que indicamos que los ficheros CSS y JS han de comprimirse (los HTML no para no entrar en conflicto con las páginas generadas por el WP-Cache modificado) y especificamos los ficheros concretos (también podríamos especificar directorios) que queremos, así mismo, almacenar en la caché:

AddOutputFilterByType DEFLATE text/css application/x-javascript
CacheEnable disk /directorio1/directorio2/style.css
CacheEnable disk /directorio1/directorio2/print.css
CacheEnable disk /directorio3/directorio4/scripts.js

Y así logramos tener todos los contenidos de nuestro blog, tanto los estáticos como los dinámicos, cacheados y comprimidos al mismo tiempo. Las imágenes, como no se suelen poder comprimir mucho más, no es necesario tratarlas.

:wq

Entradas relacionadas

12 Comentarios a “Comprimir y cachear las páginas generadas por WordPress”

  • Iván dice:

    Con este artículo y los dos anteriores han quedado muy claras las opciones que tenemos si queremos implementar el cacheo y la compresión de páginas con WP.

    Otro más que me apunto para el futuro.

    Saludos, Iván.

  • Bytecoders dice:

    Gracias Super Coco, voy a buscar algo similar para Drupal.
    Y conseguir así que se encargue Drupal de cachear los contenidos.

    Saludos, Bytecoders

  • abaca dice:

    Pues sí se nota que va más rápido ahora tu servidor. Muy interesante la entrada, y que sirva este comentario para cambiar la página cacheada y comprimida ;-)

  • @Iván, @Bytecoders, @abaca Muchas gracias por vuestros comentarios :D

  • fer dice:

    Andaba buscando información sobre este tema y me ha maravillado el detalle con que lo explicas… gracias! :)

  • @fer ¡Me alegro de que te haya gustado!

  • Víctor dice:

    ¿Y por qué no tienes WP Cache y Super Cache activadas, además de la compresión?

  • Víctor dice:

    Otra pregunta, utilizando wp super cache, hace falta tener activado zlib.output_compression = On ???

    Gracias :)

  • @Víctor No, no hace falta el zlib.output_compression y sobre por qué no tengo el WP Super Cache activado, ya lo había comentado en el blog:

    Sin embargo, estas reglas, que son las que crea el WP Super Cache 0.5.1, no funcionan si el wordpress no está instalado en el directorio raíz del blog, como se puede verificar fácilmente estudiándolas un poco.

    Por tanto, es porque las reglas que genera por defecto no funcionan bien para mi blog. Igual te interesa leer sobre el problema en este hilo de soporte de WordPress: Supercache rewrite rules incorrect if blog not at top level.

  • Víctor dice:

    Muchas gracias :)

  • Mandrake dice:

    Me gustaron estos artículos sobre caching sobretodo el que toca el modulo mod_cache con mod_deflate, pero debo comentar que el módulo de cache de Apache también aplica para contenido dinámico, ya que guarda en disco o memoria (dependiendo de la configuración) cualquier tipo de contenido que sea solicitado y enviado a través de la capa de aplicación http.

    El problema yace en aquellos contenidos dinámicos que pueden requerir mucha frecuencia de actualización, por ejemplo si una página depende de un feedback diferente de cada usuario o el contenido cambia cada 5 a 10 minutos, pero por lo general los plugins de caching de WordPress que llegué a probar tampoco son perfectos en ese sentido (debo aclarar que actualmente no uso WordPress sino Serendipity).

    Saludos!!

Trackbacks y pingbacks:

Tema LHYLE09, creado por Vicente Navarro