Een WordPress caching plugin maakt HTML aan binnen PHP en vraagt de webserver om die HTML te serveren. De Nginx FastCGI cache zit één laag eerder: nginx cachet de response van PHP-FPM in shared memory en op disk en serveert het volgende matchende verzoek rechtstreeks uit die cache, zonder ook maar één PHP-worker te starten. Bij een ongecachet verzoek loopt de keten nginx -> PHP-FPM -> WordPress -> MySQL -> WordPress -> nginx -> bezoeker. Bij een gecachet verzoek is dat nginx -> bezoeker. PHP draait gewoon niet. Dit artikel zet die cache vanaf nul op, op een eigen server, behandelt de bypass-regels die WordPress en WooCommerce nodig hebben om correct te blijven werken en laat zien hoe je hem leeggooit als de content verandert.
Hoe Nginx FastCGI cache verschilt van een WordPress caching plugin
Het zijn niet twee jasjes om dezelfde tool. Ze zitten op compleet verschillende lagen van de request-keten en die twee door elkaar halen is de meest gemaakte fout die ik tegenkom als ik WordPress-serversetups review.
Een WordPress caching plugin (WP Rocket, W3 Total Cache, WP Super Cache, LiteSpeed Cache) draait binnen PHP. WordPress boot, de plugin onderschept de response, en bij een hit schrijft hij een statisch HTML-bestand of gebruikt output buffering zodat een later verzoek een deel van de PHP-uitvoering kan overslaan. Sommige plugins gaan verder met .htaccess- of nginx-rewriteregels die bij een hit PHP helemaal omzeilen, maar het cachebestand zelf is door WordPress gemaakt.
De Nginx FastCGI cache woont in nginx zelf, in ngx_http_fastcgi_module. Die behandelt elke PHP-FPM upstream response als cachebare HTTP-content. Matcht een verzoek met een gecachete entry, dan serveert nginx hem direct vanuit een keys zone in shared memory plus een bestand op disk, en raakt PHP-FPM niet aan. Matcht het niet, dan stuurt nginx het verzoek door naar PHP, vangt de response op, slaat hem op voor de volgende keer en geeft hem terug aan de bezoeker. De caching plugin doet caching op applicatieniveau. FastCGI caching doet caching op serverniveau, voordat WordPress überhaupt is geladen. Wil je weten hoe deze laag zich verhoudt tot object cache en CDN edge, lees dan hoe WordPress caching werkt.
Configureer de cache zone in nginx.conf
De cache zone wordt eenmalig gedeclareerd in de http-context, dus niet binnen een server- of location-blok. Zet dit boven in /etc/nginx/nginx.conf of in een snippet onder /etc/nginx/conf.d/:
fastcgi_cache_path /var/cache/nginx/wordpress
levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m
max_size=1g
use_temp_path=off;
Wat elke parameter doet, volgens de fastcgi_cache_path-documentatie:
levels=1:2maakt een mappenhiërarchie van twee niveaus. Nginx hasht de cache key met MD5 en gebruikt het laatste karakter van de hash als eerste submap en de twee karakters daarvoor als tweede submap. Een gecachete entry komt dan op een pad als/var/cache/nginx/wordpress/c/29/b7f54b2df7773722d382f4809d65029c. Die hiërarchie voorkomt dat één map met tienduizenden bestanden volloopt.keys_zone=WORDPRESS:100mreserveert 100 MB shared memory voor de key-index. De nginx-docs noemen dat 1 MB ongeveer 8.000 keys vasthoudt, dus 100 MB geeft je ruimte voor zo'n 800.000 gecachete URL's. Dit is alleen de in-memory index; de gecachete responses zelf staan op disk.inactive=60mevict entries die in 60 minuten niet zijn opgevraagd, ook als ze qua TTL nog vers zijn. Wordt een entry wel opgevraagd, dan begint die timer opnieuw.max_size=1gzet een plafond van 1 GB op het schijfgebruik. Boven dat plafond gooit het cache manager-proces de minst recent gebruikte entries eruit.use_temp_path=offschrijft tijdelijke bestanden direct in de cachemap in plaats van in een aparte locatie als/var/lib/nginx/tmp. Zonder deze optie wordt elke cacheschrijfactie een rename over filesystems heen, en dat is traag en kan zelfs falen als je tmp-pad op een ander mount point staat.
Maak de map aan en geef hem aan de gebruiker waaronder nginx draait:
sudo mkdir -p /var/cache/nginx/wordpress
sudo chown www-data:www-data /var/cache/nginx/wordpress
sudo chmod 750 /var/cache/nginx/wordpress
Op RHEL-achtige systemen is die gebruiker nginx, niet www-data. Pas dat aan.
Definieer de cache key en de WordPress bypass-regels
Binnen het server-blok van je WordPress-site declareer je de key en een $skip_cache-variabele die op 1 gaat zodra caching onveilig zou zijn:
server {
listen 443 ssl http2;
server_name jouwsite.nl www.jouwsite.nl;
root /var/www/jouwsite.nl/public;
index index.php;
# Cache key: scheme + method + host + URI voorkomt botsingen tussen
# protocollen en vhosts en houdt GET en POST in losse slots.
fastcgi_cache_key "$scheme$request_method$host$request_uri";
set $skip_cache 0;
# Cache nooit POST of een niet-lege query string.
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
# Cache nooit het WordPress-dashboard, login, XML-RPC,
# de PHP entry points, feeds of sitemap.
if ($request_uri ~* "(/wp-admin/|/wp-login\.php|/xmlrpc\.php|wp-.*\.php|/feed/|sitemap.*\.xml)") {
set $skip_cache 1;
}
# Cache nooit responses voor ingelogde gebruikers, recente reageerders
# of iemand met een WooCommerce-sessie.
if ($http_cookie ~* "wordpress_logged_in|comment_author|woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session") {
set $skip_cache 1;
}
# Cache nooit de WooCommerce cart, checkout of my-account.
if ($request_uri ~* "^/(?:cart|checkout|my-account)") {
set $skip_cache 1;
}
# ... location blocks hieronder
}
Een paar dingen verdienen even uitleg.
Waarom deze cookies. WordPress zet wordpress_logged_in_<hash> voor elke ingelogde bezoeker, comment_author_<hash> voor wie net een reactie achterliet, en WooCommerce zet woocommerce_items_in_cart, woocommerce_cart_hash en wp_woocommerce_session_<hash> voor iedereen met een gevulde cart. Cache je de response van een ingelogde admin één keer en serveer je die aan anonieme bezoekers, dan zien al die bezoekers de admin toolbar boven in beeld. Cache je een WooCommerce cart-pagina, dan erft de volgende bezoeker de cart count van de vorige shopper. Cookie-gebaseerde bypass is de enige betrouwbare manier om dat te voorkomen. Header-gebaseerde bypass (waarbij PHP een Cache-Control header stuurt) werkt hier niet, want nginx moet de bypass-beslissing nemen vóórdat hij het verzoek naar PHP doorstuurt.
Waarom alle query strings uitsluiten. De cache key bevat al $request_uri, dus twee URL's die alleen in query string verschillen krijgen losse cache-entries. Toch sluit je ze uit, omdat WordPress query strings gebruikt voor previews, wachtwoord-beschermde posts, search, gepagineerde reacties en tracking-parameters. ?preview=true cachen zou een concept van een redacteur publiek toegankelijk maken. Heb je een drukke site met stabiele tracking-parameters (utm_source en zo), strip ze dan upstream in plaats van je cache te laten fragmenteren.
Waarom wp-.*\.php uitsluiten. Dat vangt wp-cron.php, wp-trackback.php, wp-activate.php en elk ander WordPress-entry point dat side effects heeft en dus nooit gecached mag worden.
Activeer de cache in het PHP-locationblok
De cache zet je per location aan met fastcgi_cache, wijzend naar de keys zone die je in http hebt gedeclareerd:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Activeer de WORDPRESS cache zone uit nginx.conf.
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 1h;
fastcgi_cache_valid 301 302 10m;
fastcgi_cache_valid 404 1m;
# Respecteer de bypass-variabele.
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
# Stampede-bescherming.
fastcgi_cache_lock on;
fastcgi_cache_lock_age 5s;
# Serve stale bij upstream-failure en refresh op de achtergrond.
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
# Debug header (in productie restrict of weg).
add_header X-Cache-Status $upstream_cache_status always;
}
fastcgi_cache_valid zet de TTL per response code. 200 1h cachet succesvolle responses een uur. 404 1m cachet not-found-responses een minuut, kort genoeg dat een nieuw gepubliceerde post snel zichtbaar wordt zonder dat PHP wordt overspoeld door 404-lookups bij een scanpoging. De add_header ... always-flag zorgt dat nginx de header ook bij errorresponses meestuurt, en dat is bij debuggen onmisbaar.
fastcgi_cache_bypass en fastcgi_no_cache zijn niet hetzelfde. fastcgi_cache_bypass zorgt dat nginx vanuit upstream haalt en niet uit cache, maar de response mag wel worden opgeslagen. fastcgi_no_cache voorkomt dat opslaan in eerste instantie. Voor WordPress wil je dat ze beide dezelfde $skip_cache-variabele evalueren, zodat een ingelogde dashboard-hit noch uit cache wordt geserveerd, noch de cache vergiftigt voor de volgende anonieme bezoeker.
PHP-FPM sizing verandert als FastCGI caching ervoor staat
Dit is het stuk dat teams onderschatten. Met een goed geconfigureerde FastCGI cache draait PHP alleen nog op cache misses: gloednieuwe URL's, de eerste hit na een TTL-expiry, alles wat de bypass-regels matcht en admin-verkeer. De rekensom voor pm.max_children schuift dus mee. De standaardformule uit de PHP-FPM-documentatie is (beschikbaar geheugen - OS-overhead) / gemiddeld geheugen per worker, maar die formule gaat ervan uit dat elk verzoek PHP raakt. Met FastCGI caching is het relevante getal niet je piek-totaal-concurrency, maar je piek-ongecachete-concurrency.
Voor een server met 2 GB, ongeveer 512 MB OS- en database-overhead en zo'n 50 MB per worker komt de rekensom zonder caching uit op (2048 - 512) / 50 ≈ 30 workers. Met caching aan is 10 tot 15 workers meestal genoeg voor een redelijk drukke site, omdat de enige verzoeken die PHP nog raken admin-werk, eerste hits na expiry en de WooCommerce-achtige pagina's zijn die je expliciet hebt uitgesloten. Zet pm.max_requests = 500 om workers periodiek te recyclen, zodat trage memory leaks niet opbouwen in langlevende processen. Voor de meeste WordPress-setups is pm = dynamic de juiste keuze; pm = ondemand bespaart RAM op rustige servers, maar betaalt cold-start latency op het eerste verzoek na idle. Voor de volledige mechaniek van pool-uitputting en de symptomen van een verzadigde pool, zie wat PHP workers zijn en waarom ze in WordPress uitgeput raken.
Verifieer de cache met X-Cache-Status
$upstream_cache_status is gedefinieerd in ngx_http_upstream_module en bestaat sinds nginx 0.8.3. Hij wordt gevuld voor elke upstream-soort, dus ook voor FastCGI. Zodra je add_header X-Cache-Status $upstream_cache_status always; toevoegt, draagt elke response een van deze waarden:
| Waarde | Betekenis |
|---|---|
MISS |
Niet in cache; nginx haalde hem op uit PHP en sloeg hem op. |
HIT |
Vanuit cache geserveerd. PHP heeft niet gedraaid. |
BYPASS |
Een fastcgi_cache_bypass-regel matchte. PHP heeft gedraaid. |
EXPIRED |
Entry zat in cache maar TTL was verlopen. PHP rerenderde hem. |
STALE |
Stale entry geserveerd omdat de upstream niet beschikbaar was. |
UPDATING |
Stale entry geserveerd terwijl een background refresh liep. |
REVALIDATED |
Entry gerevalideerd tegen PHP via een conditional request. |
De verificatie is rechttoe rechtaan. Vraag vanaf een andere machine een publieke pagina twee keer op en kijk naar de header:
curl -sI https://jouwsite.nl/over/ | grep -i x-cache-status
# X-Cache-Status: MISS
curl -sI https://jouwsite.nl/over/ | grep -i x-cache-status
# X-Cache-Status: HIT
Je weet dat de cache goed is aangesloten als het tweede verzoek HIT teruggeeft en je voor die URL geen entry meer ziet in je PHP-FPM access log. Vraag https://jouwsite.nl/wp-admin/ of een willekeurige WooCommerce cart-URL op en de header moet BYPASS tonen. Log in op WordPress en vraag een publieke post op: BYPASS, want de wordpress_logged_in_*-cookie matchte.
Belangrijk: laat X-Cache-Status niet publiek staan in productie. Hij vertelt iedereen die naar je headers kijkt precies welke URL's gecached zijn en welke paden de cache omzeilen. Dat is bruikbare reconnaissance voor een aanvaller die zoekt naar cache poisoning. Restrict de header tot vertrouwde IP's met een map-directive of haal hem weg zodra je de configuratie hebt geverifieerd.
Stampede-bescherming: fastcgi_cache_lock en fastcgi_cache_lock_age
Verloopt een populaire gecachete entry, dan zouden alle gelijktijdige verzoeken voor die entry normaal gesproken tegelijk de cache willen vullen, allemaal richting PHP. Op een drukke site is dat het thundering herd- of cache stampede-probleem en kan een WordPress-backend daar in seconden door omvallen. Twee directives voorkomen dat.
fastcgi_cache_lock on bestaat sinds nginx 1.1.12. Met de directive aan mag er per cache key maar één verzoek tegelijk de cache vullen. De andere gelijktijdige verzoeken wachten tot de cache gevuld is of tot de lock timeout afloopt. fastcgi_cache_lock_age 5s, geïntroduceerd in nginx 1.7.8, zegt vervolgens: heeft het verzoek dat de lock heeft binnen vijf seconden niet afgerond, laat dan precies één extra verzoek alsnog door naar PHP. Zo voorkom je dat de cache eindeloos blijft hangen als de upstream PHP-process vastloopt. De combinatie geeft je single-flight cache fills met een veiligheidsklep.
Er is nog een derde directive, fastcgi_cache_lock_timeout, default 5s, die bepaalt hoe lang een wachtend verzoek wacht voordat het opgeeft en alsnog ongecachet door PHP wordt afgehandeld. Sinds nginx 1.7.8 worden responses van een verzoek dat na de lock-timeout is doorgebroken niet meer in de cache opgeslagen. Dat is correct gedrag: als de lock is verlopen, is er iets mis, en dan wil je een mogelijk corrupte response niet ook nog eens cachen.
De combinatie fastcgi_cache_use_stale updating plus fastcgi_cache_background_update on (de tweede geïntroduceerd in nginx 1.11.10) is de tweede helft van je stampede-verdediging. Met allebei aan triggert de eerste binnenkomende request bij een verlopen entry een background subrequest naar PHP om de entry te verversen, en krijgt elk gelijktijdig verzoek, inclusief het verzoek dat de refresh triggerde, meteen de stale versie. Alleen de background subrequest raakt PHP. De zichtbare latency op cache-expiry zakt naar nul.
Pas wel op met één subtiele valkuil bij background updates: de background subrequest draagt geen cookies of auth headers van het oorspronkelijke verzoek. Staan je bypass-regels niet goed en is de gecachete entry van een ingelogde gebruiker, dan haalt de background refresh de anonieme versie op en slaat die op onder wat eigenlijk een gebruiker-specifieke entry moest zijn. Krijg je cookie-bypass eerst goed voordat je background update aanzet.
De cache leeggooien als content verandert
Nginx weet niet dat er net een WordPress-post is gepubliceerd. De FastCGI cache heeft geen ingebouwde invalidatiehook in WordPress. Standaard verlaat een entry de cache alleen door TTL-expiry (fastcgi_cache_valid), inactiviteit (inactive), LRU-evictie bij max_size of een expliciete purge. Publiceer je een post en doe je verder niets, dan zien bezoekers die post pas als de cache-TTL afloopt. Dat is geen bug; dat is de trade-off die je hebt geaccepteerd in ruil voor het feit dat PHP bij hits nooit meer draait.
Er zijn twee praktische manieren om te purgen.
Optie 1: de Nginx Cache plugin van Till Krüss. Deze route raad ik de meeste WordPress-sites aan, omdat hij werkt met de standaard open-source nginx en geen custom modules nodig heeft. De Nginx Cache plugin, versie 1.0.7 sinds november 2024, hookt in op WordPress save events (post publish, post update, post delete) en purgt de cache door de inhoud van de cachemap te verwijderen via de WordPress Filesystem API. De configuratie zit onder Tools > Nginx in het WordPress-dashboard: je wijst hem naar het cache zone-pad, in dit voorbeeld /var/cache/nginx/wordpress, en dat is het.
Het mechanisme om in je hoofd te houden: deze plugin purgt de hele cache zone, niet individuele URL's. Elke content-update gooit elke gecachete pagina op de site weg. Voor een site met weinig pagina's en weinig updates is dat prima. Voor een drukke site met veel publicatie betekent het dat je periodiek je hele cache verliest en de volgende golf bezoekers tegelijk een cache miss is, en dat is precies het stampede-probleem waar fastcgi_cache_lock voor bestaat. Voor de plugin om te werken moet PHP kunnen schrijven in de nginx-cachemap, dus draaien nginx en PHP-FPM als dezelfde gebruiker (de default op Debian en Ubuntu is dat ze allebei www-data zijn) of heeft de gebruiker waaronder PHP draait schrijfrechten op het cachepad. Daarnaast moet de Filesystem API kunnen werken zonder om FTP-credentials te vragen, dus zet define('FS_METHOD', 'direct'); in wp-config.php als dat er nog niet staat.
Optie 2: per-URL purgen met ngx_cache_purge. Nginx Plus heeft een commerciële fastcgi_cache_purge-directive die een key meekrijgt en precies die entry verwijdert. Voor open-source nginx biedt de third-party ngx_cache_purge-module van FRiCKLE dezelfde mogelijkheid, maar dan moet je nginx hercompileren met de module statisch erin. Ga je deze route op, dan wil je een plugin die voor specifieke URL's een purge-endpoint aanroept op nginx (de post die veranderde, de homepage, de categoriearchieven, de sitemap) in plaats van alles weg te gooien. De complexiteit is wel echt: je onderhoudt nu een eigen nginx-build, een upgradepad dat de module bij elke nieuwe nginx-versie opnieuw moet bouwen, en configuratie die de WordPress- en nginx-kant in sync moet houden. De moeite waard voor een drukke nieuwssite; overkill voor een mkb-site.
Of de purge van de plugin daadwerkelijk werkt, controleer je door na het publiceren van een nieuwe post een gecachete URL op te vragen en naar X-Cache-Status te kijken. Het eerste verzoek na de purge moet MISS tonen, het tweede HIT.
Cache hit ratio monitoren via access logs
De simpelste manier om te weten of je cache écht iets nuttigs doet, is $upstream_cache_status op elk verzoek loggen en aggregeren. Voeg een custom log-format toe in nginx.conf:
log_format cache_log '$remote_addr - $upstream_cache_status [$time_local] '
'"$request" $status $body_bytes_sent';
server {
# ...
access_log /var/log/nginx/wordpress-cache.log cache_log;
}
Reload nginx, laat het log even vollopen en tel per status:
awk '{print $4}' /var/log/nginx/wordpress-cache.log | sort | uniq -c | sort -rn
Een gezonde WordPress-site met FastCGI caching laat HIT domineren, met BYPASS voor admin-verkeer en uitgesloten paden. Is MISS hoog, dan zijn je TTL's te kort, is je inactive-waarde te agressief, of is max_size te klein en wordt de cache sneller geëvict dan hij vult. Is EXPIRED hoog ten opzichte van HIT, dan past je TTL niet bij je publicatieritme. Zie je regelmatig STALE, dan is je PHP-backend instabiel en kun je beter onderzoeken waarom upstream-errors überhaupt nginx bereiken.
Een bruikbare baseline om naar te streven op een contentsite die niet ieder uur verandert is 90% of hoger HIT-ratio bij normaal verkeer. WooCommerce-stores en sites met veel ingelogd verkeer zitten lager, want meer verzoeken bypassen de cache terecht.
Troubleshooting van stale content en ontbrekende uitsluitingen
Een paar patronen die ik vaak voorbij zie komen.
Bezoekers zien nog steeds de oude versie van een post nadat ik heb gepubliceerd. Dan is óf de Nginx Cache plugin niet geconfigureerd (check Tools > Nginx in het dashboard), óf PHP kan niet schrijven in de cachemap. Tail /var/log/nginx/error.log terwijl je een save in WordPress triggert en kijk naar permissieerrors. Verifieer dat de map dezelfde owner heeft als waaronder PHP-FPM draait en dat FS_METHOD op direct staat in wp-config.php. Als tussenoplossing kun je de cache handmatig leeggooien met sudo find /var/cache/nginx/wordpress -mindepth 1 -delete.
De admin toolbar verschijnt boven publieke pagina's voor anonieme bezoekers. Dan is er een ingelogde response gecached en wordt die nu aan iedereen geserveerd. De cookie-bypass ontbreekt of is verkeerd. Check dat if ($http_cookie ~* "wordpress_logged_in") in je server block staat en purge de cache één keer. Dit gebeurt meestal als iemand een generieke nginx-config heeft overgeschreven en de cookie-regex is vergeten. Om herhaling te voorkomen: log in als admin, vraag een publieke URL op en check X-Cache-Status. Die moet BYPASS tonen. Zegt hij HIT of MISS, dan is je bypass kapot.
De WooCommerce cart count klopt niet voor bezoekers. Zelfde root cause: een cart-state response is gecached. Verifieer dat woocommerce_items_in_cart, woocommerce_cart_hash en wp_woocommerce_session allemaal in de cookie-regex staan en dat ^/(?:cart|checkout|my-account) in de URI-uitsluiting zit. Purge één keer en check met curl.
X-Cache-Status ontbreekt in responses. Of je server block bevat geen add_header X-Cache-Status $upstream_cache_status always;, of een andere module strip headers eraf. Check je volledige nginx-configuratie met nginx -T en zoek de directive op in de gerenderde output voor het juiste server block.
MISS op elk verzoek, ook bij dezelfde URL twee keer achter elkaar. Dan wordt er niets in de cache zone geschreven. Veelvoorkomende oorzaken: de cachemap bestaat niet of is niet schrijfbaar voor de nginx-gebruiker, fastcgi_cache_path staat in een server-blok in plaats van http (en dat is ongeldig), de upstream response stuurt een Set-Cookie-header mee (wat caching standaard blokkeert), of fastcgi_cache_valid matcht niet met de response code. Check nginx -T of het cachepad op het juiste niveau is gedeclareerd, draai sudo -u www-data touch /var/cache/nginx/wordpress/test om schrijfbaarheid te verifiëren en bekijk de response met curl -sI om te zien of PHP onverwacht Set-Cookie-headers stuurt op pagina's die cachebaar zouden moeten zijn. Een veelvoorkomende WordPress-boosdoener is een plugin die setcookie() op elke pagina aanroept; je moet dan óf de aanroep van de plugin verwijderen óf fastcgi_ignore_headers Set-Cookie zetten (voorzichtig, want dat kan echte per-user state maskeren).
Volledige eindconfiguratie
Hier is de samengestelde configuratie in de volgorde waarin hij hoort, zodat je hem niet hoeft te reconstrueren uit de secties hierboven.
/etc/nginx/nginx.conf (binnen het http-blok):
fastcgi_cache_path /var/cache/nginx/wordpress
levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m
max_size=1g
use_temp_path=off;
log_format cache_log '$remote_addr - $upstream_cache_status [$time_local] '
'"$request" $status $body_bytes_sent';
/etc/nginx/sites-available/jouwsite.nl:
server {
listen 443 ssl http2;
server_name jouwsite.nl www.jouwsite.nl;
root /var/www/jouwsite.nl/public;
index index.php;
access_log /var/log/nginx/jouwsite-cache.log cache_log;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "(/wp-admin/|/wp-login\.php|/xmlrpc\.php|wp-.*\.php|/feed/|sitemap.*\.xml)") {
set $skip_cache 1;
}
if ($http_cookie ~* "wordpress_logged_in|comment_author|woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session") {
set $skip_cache 1;
}
if ($request_uri ~* "^/(?:cart|checkout|my-account)") {
set $skip_cache 1;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 1h;
fastcgi_cache_valid 301 302 10m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_lock on;
fastcgi_cache_lock_age 5s;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
# In productie weghalen of restricten.
add_header X-Cache-Status $upstream_cache_status always;
}
}
Apply met:
sudo nginx -t && sudo systemctl reload nginx
Installeer dan de Nginx Cache plugin in WordPress, wijs hem onder Tools > Nginx naar /var/cache/nginx/wordpress en je hebt een full-page cache op serverniveau die PHP bij hits volledig omzeilt, zichzelf op de achtergrond ververst, stampedes opvangt, WordPress- en WooCommerce-sessiestate respecteert en automatisch purgt als je content verandert.