Optimiser WordPress avec NGINX et W3 Total Cache – Suite
Ce post continue le précédent sur l’optimisation de WordPress avec Nginx et W3 Total Cache (W3TC). Après avoir passé une configuration standard et vérifié le bon paramétrage de la compression Gzip, il est temps de passer à la configuration de W3TC. Est-il besoin de rappeler que c’est le bon moment pour réaliser un back-up de la de données? A priori les opérations suivantes sont sans danger, mais on ne sait jamais.
Paramètres de W3TC
Plutôt qu’une longue explication, j’ai préféré présenter des captures d’écran.
Le page cache est le plus important. Avec l’option « disk enhanced » W3TC va créer une version html statique des pages dans l’arborescence du site à l’emplacement /wp-content/cache/page_enhanced/
Méfiance avec le minify, il peut potentiellement casser un thème mal codé, bien vérifier le résultat avant d’adopter les réglages. Si tout se passe bien, w3TC place les fichiers « minifiés » à l’emplacement /wp-content/cache/minify/
. Deux paramètres importants:
- disk : place les fichiers « minifiés » dans le répertoire /wp-content/cache/minify/ et les rend accessible à Nginx
- auto : agressif et pousse le minify à regrouper les css et js sous quelques fichiers uniques au nom barbare (ZY7RDYQwDEMXggYEEzACug…a8zLa-7PeD4r-gI.css)
Ce dernier point a permis de réduire de 65 à 25 le nombre d’éléments à charger pour afficher la page de mon blog. Un navigateur typique ne charge que 2 fichiers à la fois, cette manipulation économise donc sur les temps de connect/send/wait. Si vous rencontrez l’erreur suivante, il faut aller activer l’option « Disable minify automatic file name length test » sur la page « minify » de W3TC:
J’utilise pour mon site le YUI Compressor, il est recommandé par Google et semble être plus efficace que les autres. Un simple apt-get install yui-compressor
suffit sous debian pour l’installer, par contre, il faudra aller préciser le « path to java executable : /usr/bin/java » et le « path to jar file : /usr/share/yui-compressor/yui-compressor.jar » dans l’onglet Minify de W3TC.
Il ne reste plus que le « object cache » et « DB cache ». Memcache ou APC fonctionnent a priori sans soucis pour les deux et n’interagissent pas avec Nginx.
Une fois les changements enregistrés, W3TC va créer un fichier nginx.conf
à la racine du site web. Le mien ressemble à ça.
Intégrer la config Nginx de W3TC
La première méthode la plus simple consiste à rajouter dans le fichier de configuration nginx du serveur web un simple include $document_rootnginx.conf;
juste au dessus du premier bloc « location ». Et ça marche, il suffit de faire un tour dans le répertoire /wp-content/cache/page_enhanced/phil.writesthisblog.com/
pour s’en convaincre. Chaque fois qu’un visiteur (non enregistré) visite une page du WordPress, son contenu apparaît dans le cache. Aux visites suivante, le cache est alors disponible.
Le problème est que la config proposée par W3TC est très mal rédigée. Rewrite dans tous les sens, plein de IFs, et elle ne fonctionne pas si bien que ça…
Améliorer la propre config Nginx à partir de celle de W3TC
L’idée principale est de reprendre les tests IFs proposés dans le nginx.conf de W3TC, et de remplacer tous les rewrites par des try_files dans des blocs location
, ce qui permet de supprimer quelques IFs au passage.
J’ai mis en commentaires dans la configuration les explications sur ce que fait chaque bloc. Les IFs restants décident si le contenu caché doit être servi. Tout se joue alors dans le bloc « location / { … } ». Nginx teste d’abord si le contenu existe, si ce dernier ne doit pas être servi, le ‘cache null’ assure que rien ne sera trouvé dans le cache. Ensuite, nginx teste normalement pour le contenu avec $uri
et $uri/
et sinon renvoie sur php5-fpm avec /index.php
. W3TC ne crée la version statique d’une page que lorsqu’elle est accédée via /index.php
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | server { listen 80; server_name phil.writesthisblog.com; access_log /var/log/nginx/wp_philwritesthisblog_access.log; error_log /var/log/nginx/wp_philwritesthisblog_error.log; root /var/www/wp-phil-writesthisblog; index index.php; # La liste du contenu a ne pas servir location ~ /\. { access_log off; log_not_found off; deny all;} location = /wp-content/backup-db { access_log off; log_not_found off; deny all;} location = /wp-config.php { access_log off; log_not_found off; deny all;} location ~ ^/[^/]+/uploads/.*\.php.? { access_log off; log_not_found off; deny all;} # on ne vas pas executer du php dans le repertoire upload wtf!? location = /favicon.ico { log_not_found off; access_log off; } # pas besoin garder dans les logs les accès au favicon et au robots.txt location = /robots.txt { log_not_found off; access_log off; } # Integrer les 4 rewrites dans un "location" unique # Afin de limiter le nombre de test par requetes # Plugin Google XML Sitemap location ~ ^/sitemap(.*)\.(xml|html)(\.gz)? { expires -1; default_type text/xml; add_header Cache-Control "private, max-age=0"; rewrite ^/sitemap([-_]([a-zA-Z0-9_-]+))?\.xml$ "/index.php?xml_sitemap=params=$2" last; rewrite ^/sitemap([-_]([a-zA-Z0-9_-]+))?\.xml\.gz$ "/index.php?xml_sitemap=params=$2;zip=true" last; rewrite ^/sitemap([-_]([a-zA-Z0-9_-]+))?\.html$ "/index.php?xml_sitemap=params=$2;html=true" last; rewrite ^/sitemap([-_]([a-zA-Z0-9_-]+))?\.html.gz$ "/index.php?xml_sitemap=params=$2;html=true;zip=true" last; } # Debut des tests pour decider si on sert le contenu cache set $cache_uri $request_uri; # Les requetes POST doivent toujours etre envoyee vers le PHP if ($request_method = POST) { set $cache_uri 'null cache'; } if ($query_string != "") { set $cache_uri 'null cache'; } # Ne sert pas le contenu cache pour les utilisateurs enregistres (option "Don't cache pages for logged in users") # W3TC ne cache que le contenu qui s'affiche aux visiteurs du site if ($http_cookie ~* "(comment_author|wp\-postpass|w3tc_logged_out|wordpress_logged_in|wptouch_switch_toggle)") { set $cache_uri 'null cache'; } # Ne sert pas le contenu cache pour les uris contenant les elements suivants # Plus par securite qu'autre chose. W3TC n'est pas cense avoir cree de cache pour ce contenu de toutes facons # Les recherches (index.php?s=...) ne sont jamais cachees if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { set $cache_uri 'null cache'; } # Gere le cache du mode preview de W3TC. Mais est-ce pertinant de cacher # le contenu en mode preview? A degager si vous ne vous en servez pas. # En plus, le comportement de W3TC est bizarre la dessus. set $w3tc_preview ''; if ($http_cookie ~* "(w3tc_preview)") { set $w3tc_preview '_preview'; set $cache_uri $request_uri; } # Ici on teste pour voir si le contenu cache existe (1er test). Si oui, on le passe # Si non, on traite normalement la requête. W3TC creera le cache à ce moment la # Si $cache_uri=='null cache', le test renverra toujours un echec. location / { try_files /wp-content/cache/page_enhanced/${host}${cache_uri}_index${w3tc_preview}.html $uri $uri/ /index.php ; } # Passe le contenu javascript minifie. S'il n'existe pas, il le cree à la volee location ~ ^/wp-content/cache/minify/[^/]+/(.*\.js)$ { types {} default_type application/x-javascript; try_files $uri /wp-content/plugins/w3-total-cache/pub/minify.php?file=$1; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; expires 1w; } # Passe le contenu css minifie. S'il n'existe pas, il le cree a la volee location ~ ^/wp-content/cache/minify/[^/]+/(.*\.css)$ { types {} default_type text/css; try_files $uri /wp-content/plugins/w3-total-cache/pub/minify.php?file=$1; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; expires 1w; } # Les 2 blocs precedents pourraient etre reduits a un seul. I 2 blocs pour etre plus propre # Et rajouter le default type css ou js # Permet a WordPress de passer ses propres messages d'erreur fastcgi_intercept_errors off; # Defini la politique de cache pour les navigateurs location ~* \.(js|css|gif|jpg|jpeg|png|txt|xml|ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|ico|zip|tgz|gz|rar|bz2|doc|xls|ppt|tar|mid|midi|wav|bmp|rtf)$ { expires max; add_header Vary "Accept-Encoding"; add_header Pragma public; add_header Cache-Control "public, must-revalidate, proxy-revalidate"; } # Renvoie les requetes au moteur php5-fpm location ~ \.php$ { try_files $uri $uri/ /index.php; include php_params; } } |
Je rajoute aussi, dans les différents blocs « location » les paramètres de cache pour les navigateurs. En utilisant le net panel de Firebug et son propre navigateur, on voit qu’après une visite sur la première page, les contenus .css, .js et la plupart des images ne sont plus rechargés. N’hésitez pas à adapter ces paramètres en fonction de vos propres besoin.
On peut maintenant tester
Si tout s’est bien passé, on peut constater que chaque passage d’un visiteur (c.a.d non enregistré) sur une page du site, va faire générer par W3TC le contenu statique dans le répertoire /wp-phil-writesthisblog/wp-content/cache/page_enhanced/phil.writesthisblog.com/
. Chaque visiteur suivant se verra servir le contenu directement depuis le cache. L’intérêt est immédiat en testant son blog sur PingDom, l’ordre de grandeur des « Wait times » pour les fichiers sont les suivants:
- non caché donc généré par WordPress : 100 à 600ms voir pire
- caché mais W3TC mais en mémoire : ~70ms
- caché sur disque et servi par Nginx : ~20ms
On peut aussi remarquer que W3TC invalide bien le cache en rajoutant le suffixe .old aux fichiers. Naturellement, ils ne seront pas servis.
Mais plus intéressant encore ! Supposons que le moteur PHP ou MySQL de votre serveur se plante. Ça arrive malheureusement. Ça se teste avec un service php5-fpm stop
, tout le contenu déjà en cache continue à être servi. Bien sûr, la partie dynamique du site est indisponible, plus possible de se loguer sur le serveur etc. mais en attendant les visiteurs continuent à surfer comme si de rien n’était.
Pour Conclure
A ce stade, on dispose maintenant d’un serveur relativement bien optimisé et résiliant.
-
Gestion du cache navigateur par nginx
Contenu statique délivré par le cache
Résistant à un crash PHP/MySQL
Le blanc dans les waterfall est du au chargement du DOM par le navigateur. Là, il n’y a pas grand chose à faire côté serveur, cela dépend essentiellement du thème utilisé et du CPU de l’utilisateur.
Malgré tout, il reste encore quelques optimisations possibles, dont la principale consiste à précharger le cache au lieu d’attendre les requêtes clients. La suite au prochain post.
Une pensée sur “Optimiser WordPress avec NGINX et W3 Total Cache – Suite”