6 trucos para forzar WordPress al límite
La verdad es que me sorprendió el interés que despertó una de las entradas de la semana pasada, 13 cosas que debes cambiar inmediatamente en tu WordPress. Resulta que hay gente que lee este blog. Y de alguna manera me quedé con ganas de explicar algún truquillo más que suelo aplicar, pero me resultaba difícil poder encasillarlo como “muy básico, que tu abuela pueda entenderlo”. Hoy venimos con esas. Cometeré la osadía de intentar demostrar que las segundas partes no tienen porqué desmerecer a las primeras. A ver qué te parecen los 6 trucos para forzar tu WordPress al límite.
Las páginas dinámicas como las que genera WordPress, por definición, requieren de procesamiento por parte del servidor web. Cuando tratamos de optimizar los recursos, lo que intentamos precisamente es evitar esa sobrecarga, convirtiendo el contenido dinámico en estático. Intentamos que sea lo más estático posible para evitar cualquier cálculo que sature nuestros servidores. Esta entrada va sobre eso. Sobre cómo buscar un compromiso entre la cantidad de lectores que tenemos o esperamos tener, la frescura o dinamismo del contenido que debemos servir y la capacidad de respuesta de los recursos de los que disponemos para saciar a nuestros visitantes.
La configuración que propongo son sólo algunas de las técnicas utilizadas para maximizar la cantidad de lectores que nuestra web puede soportar, utilizando los mínimos recursos necesarios.
Utilizaré un lenguaje plano y sencillo, para que nadie se pierda. Por favor, puristas de los tecnicismos, sed permisivos.
1. Opcode caché
Tratando de simplificar al máximo, cuando un archivo PHP se ejecuta el proceso es el siguiente:
- análisis
- compilación
- ejecución.
Sin reparar demasiado en explicaciones, el opcode caché consiste en cachear scripts PHP compilados en bytecode. De esta forma evitamos los dos primeros pasos, con el coste y retardo que conlleva cada uno de ellos. Si os preguntáis por el beneficio de hacer esto, es muy sencillo: brutal. La diferencia de rendimiento es de entre un x2 y un x10 respecto a no utilizarlo. La única pega, es que generalmente, los servidores compartidos no lo ofrecen. La mayoría de veces se suele necesitar un servidor dedicado, y sí amigo, es más caro.
Aun con ese handicap, es un punto muy a tener en cuenta en nuestro objetivo de forzar nuestra web hasta límites incómodos.
Para los que tenéis servidor dedicado, esto va a la linea de comandos:
sudo apt-get install php-apc
#2. Caché de navegador
Otro fundamental. Consiste en configurar nuestro servidor web de tal manera que indique al navegador con qué frecuencia cambia el contenido. En el caso de fotos, audio, video o cualquier contenido multimedia, el archivo en sí no cambia nunca. No se genera dinámicamente. La foto aurken.jpg siempre será la misma imagen. Cuando actualizamos el contenido multimedia, añadimos nuevas fotos con nuevos nombres, pero el archivo aurken.jpg queda intacto. Esto permite que podamos indicar al navegador del usuario que el contenido es para siempre. Que una vez lo cargue en la web, siempre utilice la misma versión.
Como parece lógico, esto influye notablemente tanto en el tiempo de carga de la página como en la utilización de recursos, siempre limitados. Hace que no haya que descargar todas las imágenes, flv, swf, css, js y demás archivos estáticos una y otra vez. La primera vez que entremos a la web, la página tardará el tiempo que necesite para servir todo el contenido. Pero en las siguientes páginas o visitas, la carga del contenido estático será prácticamente inmediato.
El cambio para indicar a nuestro servidor que actúe de esa manera es el siguiente:
Servidor Apache (la mayoría de servidores compartidos)
<ifModule mod_expires.c>
ExpiresActive On
ExpiresDefault A604800
# Cacheamos durante 28 días
<FilesMatch ".(ico|gif|jpe?g|png|flv|pdf|swf|mov|mp3|wmv|ppt)$">
ExpiresDefault A2419200
</FilesMatch>
<FilesMatch ".(xml|txt|js|css)$">
ExpiresDefault A36000
</FilesMatch>
<FilesMatch ".(php|cgi|pl|html|htm)$">
ExpiresDefault A0
</FilesMatch>
</ifModule>
# Headers
<ifModule mod_headers.c>
<FilesMatch ".(ico|gif|jpe?g|png|flv|pdf|swf|mov|mp3|wmv|ppt)$">
Header append Cache-Control "public"
</FilesMatch>
<FilesMatch ".(xml|txt|js|css)$">
Header append Cache-Control "private, must-revalidate"
</FilesMatch>
# Sin caché los archivos PHP y HTML
<FilesMatch ".(php|cgi|pl|html|htm)$">
Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
Header set Pragma "no-cache"
</FilesMatch>
</ifModule>
Basta con incluir esas lineas al final del archivo .htaccess del directorio raíz y conseguiremos lo que buscamos.
Servidor Nginx
# Cacheamos durante 30 días las siguientes extensiones
location ~* \.(?:ico|gif|jpe?g|png|flv|pdf|swf|mov|mp3|wmv|ppt)$ {
log_not_found off;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
#3. Output caché
Cuando cargamos una página cualquiera, el contenido dinámico se suele obtener de una base de datos. WordPress utiliza la base de datos relacional MySQL para el cometido. Eso significa que cada usuario que entra en la web, exige resultados a la base de datos. Y la base de datos, cuando se le pide demasiado, sufre. En este punto lo que buscamos es aliviar, casi eliminar el dolor de MySQL. Y cortamos por lo sano.
Al acceder a una página, ésta carga principalmente un archivo HTML que previamente ha sido generado por PHP. Y junto a este archivo, se cargan las fotos y recursos que necesitamos para acabar de dar la imagen y aspecto final a la página.
Para la tarea utilizamos WP Super Cache, que aporta notables resultados. WP Super Cache se encarga de guardar en caché páginas dinámicas. Convierte una página generada dinámicamente en un archivo HTML estático. “Supervisa” los posts, comentarios y demás cambios que se dan en el contenido. Cuando el contenido cambia, WP Super Cache borra la versión anterior generando una nueva. Hasta que detecta un nuevo cambio, servirá el contenido como si se tratara de un HTML estático. Éste es su gran beneficio. No sólo evita el procesamiento sino que quita del medio a PHP. No se procesa código PHP alguno.
#4. Compresión GZIP, mod_deflate
El contenido, como cualquier archivo virtual tiene un tamaño. Al transferir ese contenido por Internet, cuanto menor sea el tamaño mejor. Para eso utilizamos la compresión. Como su nombre indica, comprime la información para que sea más rápido y evidentemente, más compacta.
Servidores Apache (añadir al final del archivo .htaccess)
# Compresión GZIP
<ifModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/xml text/css text/plain
AddOutputFilterByType DEFLATE image/svg+xml application/xhtml+xml application/xml
AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml
AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript application/json
AddOutputFilterByType DEFLATE application/x-font-ttf application/x-font-otf
AddOutputFilterByType DEFLATE font/truetype font/opentype
</ifModule>
Servidores Nginx
# Habilitar compresión GZIP
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1100;
gzip_buffers 16 8k;
#gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
#5. Combinar archivos js, css
Cada archivo que se carga para mostrar una página supone una petición de respuesta por parte del servidor. Por lo general, al organizar el código, utilizar plugins o añadir librerías, solemos acabar cargando 5 archivos javascript y otros tantos css. Y eso, significa 10 peticiones. Para ser más incisivo, sobran 8. No necesitamos más que un archivo con todo el código javascript y otro con el código css. Ahora bien, imagina esto con 100 personas clickeando en tu web y generando un 500% de carga. ¿No habíamos quedado que teníamos recursos limitados?
Podemos solucionar esto a mano (lo tenía que comentar para los valientes) o a través de un buen plugin, como WP Minify. Su instalación no tiene mayor misterio.
Actualización 14 Octubre 2014: El desarrollador ha dejado de actualizar este plugin. Tanto WP Minify Fix como Better WordPress Minify pueden ser buenas alternativas.
#6. Subdominios para archivos estáticos
En la mayoría de webs modernas, se guarda información referente al usuario. Son las famosas cookies, importantes para generar el contenido de la web, pero no para mostrar archivos estáticos. Recordemos que los archivos como imágenes, audio, video, etc no cambian. Es decir, la información de las cookies no les sirve para nada, es inútil. El problema es que las cookies se envían a través de todo el dominio. Por eso se hace lo siguiente:
- www.aurken.net crea cookies que estarán disponible en cualquier página del subdominio www.
- assets.aurken.net sirve contenido estático en el subdominio assets, sin cookies. Desde aquí se servirán todas los archivos estáticos de WordPress y el theme en cuestión.
- uploads.aurken.net sirve el contenido estático de la carpeta /wp-content/uploads/ bajo el subdominio uploads, también sin cookies.
Con esto conseguimos que el navegador del usuario no descargue innecesariamente las cookies de los archivos estáticos. El impacto real se ve cuando al cargar una página hay 10 imágenes, y 10 archivos css/js/video/audio. Nos estaríamos ahorrando las 20 cookies que irían asociadas a cada uno de los archivos.
Lo mismo, permite maximizar los recursos de los que disponemos para que nuestro WordPress esté siempre en plenas facultades para responder ante cualquier petición.
Estos cambios deberían ser suficientes para empujar a tu WordPress hacia los límites de tu servidor. Y funcionan, creedme que sí. Al menos lo suficiente como para servir a miles (literalmente) de usuarios concurrentes.
Lo ideal aún así, sería que estas soluciones no fueran suficientes para tu WordPress. Estaríamos hablando entonces de un bendito problema.