Comprimir y cachear las páginas generadas por WordPress
Las dos entradas anteriores:
- Probando el mod_deflate de Apache
- Usando el mod_cache de Apache para que el mod_deflate no incremente la carga del servidor
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:
- Parches para WP-Cache
- Modifying WP-Cache 2.0 to generate and cache gzipped output once and serve it multiple times
- WP-Cache + gzip without excessive CPU overhead, yay!
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:
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
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.
Gracias Super Coco, voy a buscar algo similar para Drupal.
Y conseguir así que se encargue Drupal de cachear los contenidos.
Saludos, Bytecoders
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
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!
¿Y por qué no tienes WP Cache y Super Cache activadas, además de la compresión?
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: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.
Muchas gracias
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!!