De WordPress REST API is krachtig, maar in productie loopt hij vaker tegen performance-muren aan dan menig developer denkt. Een simpele GET /wp/v2/posts kan in een slecht getuned WordPress-setup zomaar 600ms tot 1,5 seconde kosten. En dat is voordat je er een headless frontend, mobiele app of integratie op zet die tientallen requests per seconde genereert.
In dit artikel duiken we dieper in de REST API performance van WordPress: waar de bottlenecks zitten, hoe je ze meet en welke technieken écht verschil maken. Of je nu een headless setup bouwt of je API gebruikt voor interne tooling, met de juiste optimalisaties haal je responstijden onder de 100ms.
Waarom de REST API traag kan zijn
Het fundamentele probleem: elke REST-request doorloopt de volledige WordPress-bootstrap. Dat betekent wp-load.php laadt, alle actieve plugins worden geïnitialiseerd, je thema's functions.php wordt uitgevoerd en hooks zoals init en wp_loaded vuren af. Pas daarna begint het eigenlijke afhandelen van de API-call.
Voor een developer die gewend is aan een kale Node.js of Go API voelt dat verspilling. En dat is het vaak ook. Een gemiddelde WordPress-installatie met 20 plugins doet al 50-100ms aan bootstrap voordat er ook maar één database-query is gedraaid voor je endpoint.
Daar bovenop komen de typische WordPress-zwaartepunten: trage queries, autoloaded options en ongecachete metadata. We hebben de achtergrond daarvan eerder uitgebreid behandeld in Waarom je WordPress traag is (en hoe je het fixt) en Autoloaded options: stille performance killer.
De anatomie van een REST-request
Als je een request op /wp-json/wp/v2/posts?per_page=10 stuurt, gebeurt er ongeveer dit:
- NGINX of Apache routeert het request naar PHP-FPM
- WordPress laadt de volledige bootstrap
- De REST controller matcht de route en parsed parameters
WP_Querywordt uitgevoerd met de opgegeven filters- Voor elke post wordt metadata, auteur en featured image opgehaald
prepare_item_for_responsetransformeert elke post naar JSON- Response wordt geserialiseerd en teruggestuurd
Stap 5 is berucht: voor tien posts betekent dat al gauw 30-50 extra queries als caching niet goed staat. En stap 6 triggert filters zoals the_content en the_title, wat plugins kansen geeft om zich nog eens te laten gelden.
Meten voor je optimaliseert
Zonder data is optimaliseren gokken. Voordat je wat dan ook aanpast, breng je in kaart waar de tijd verloren gaat. Gebruik deze tools:
- Query Monitor met de REST API-panelen geeft je per request inzicht in queries, hooks en PHP-tijd
- New Relic of Blackfire voor production profiling van echte requests
SAVEQUERIESinwp-config.phpom queries te loggen via$wpdb->queries- Custom
X-Request-Timeheaders die je zelf toevoegt via een must-use plugin
Meer over dit proces lees je in WordPress performance bottlenecks herkennen. Het principe is simpel: meet eerst, dan pas optimaliseren. En meet daarna opnieuw om te checken of je aanpassing echt hielp.
Wat is een goede responstijd?
Voor een cold request (niet-gecachet) zit je in een gezonde setup rond de 150-300ms. Met object caching warm haal je 50-150ms. Alles boven de 500ms wijst op een concreet probleem: een ontbrekende index, te veel autoloaded data of een plugin die zich bemoeit met elk request.
Object caching als fundament
Zonder een persistent object cache is elke REST-request ongecachet. wp_cache_get valt dan terug op een runtime array die na het request weer weg is. Dat betekent dat identieke calls achter elkaar exact dezelfde queries draaien.
Met Redis of Memcached als drop-in object cache verandert dat drastisch. Opties worden gecachet, post-objecten, user-meta, termen - alles wat via de WordPress cache-API loopt. We hebben de opzet stap voor stap beschreven in Object caching met Redis in WordPress.
Voor de REST API is dit geen luxe maar een must. Een standaard posts-endpoint is met object caching vaak 3-5x sneller op warme requests.
Transients voor endpoint-niveau caching
Voor custom endpoints kun je een stap verder gaan: cache de volledige response. Bijvoorbeeld:
add_action('rest_api_init', function () {
register_rest_route('mijnsite/v1', '/dashboard', [
'methods' => 'GET',
'callback' => function ($request) {
$cache_key = 'dashboard_' . md5(serialize($request->get_params()));
$cached = get_transient($cache_key);
if ($cached !== false) {
return rest_ensure_response($cached);
}
$data = build_dashboard_data();
set_transient($cache_key, $data, 5 * MINUTE_IN_SECONDS);
return rest_ensure_response($data);
},
'permission_callback' => '__return_true',
]);
});
Let op de nuances tussen transients en object cache - die behandelen we in Transients vs object cache: wanneer gebruik je wat.
Full page caching voor publieke endpoints
Voor publieke, niet-gepersonaliseerde endpoints is full page caching op HTTP-niveau de zwaarste vorm van optimalisatie. Een gecachete response wordt door NGINX direct teruggegeven, zonder dat PHP überhaupt wordt aangeroepen. Dat brengt responstijden van milliseconden naar single digits.
Een voorbeeldconfiguratie in NGINX:
location ~ ^/wp-json/wp/v2/(posts|pages|categories) {
set $skip_cache 0;
if ($http_authorization) { set $skip_cache 1; }
if ($arg_nocache) { set $skip_cache 1; }
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 5m;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
}
Dit slaat alleen GET-requests op de genoemde routes op, bypasst caching bij een Authorization-header en invalideert na 5 minuten. Voor de bredere context, zie Full page caching: NGINX vs plugins.
Cache invalidation
Het klassieke probleem: wanneer gooi je de cache weg? Bij het updaten van een post wil je dat /wp/v2/posts en /wp/v2/posts/123 vers zijn. Hook daarom op save_post, deleted_post en transition_post_status om relevante cache-keys te invalideren.
Voor NGINX fastcgi_cache gebruik je de ngx_cache_purge-module of je sleutelt de cache per post-ID zodat je gericht kunt purgen. Verwacht hier wat engineering werk - cache invalidation is berucht als een van de moeilijkste problemen in software.
Queries optimaliseren
De REST API gebruikt onder water WP_Query. Hoe die precies werkt hebben we uitgelegd in Hoe werkt WP_Query onder de motorkap?. Voor performance zijn een paar praktische aandachtspunten:
- Zet
no_found_rowsvia derest_post_queryfilter als je geen paginering nodig hebt - dat scheelt eenSQL_CALC_FOUND_ROWSquery - Gebruik
fieldsom alleen velden op te halen die je nodig hebt - Vermijd
meta_queryop grote datasets zonder een proper geïndexeerde meta_key - Overweeg een
_embedparameter alleen te gebruiken als je echt embedded data nodig hebt
Een concrete tweak:
add_filter('rest_post_query', function ($args, $request) {
if ($request->get_param('lite')) {
$args['no_found_rows'] = true;
$args['update_post_meta_cache'] = false;
$args['update_post_term_cache'] = false;
}
return $args;
}, 10, 2);
Met ?lite=1 schakel je dure bijwerkingen uit voor endpoints waar je die data toch niet gebruikt.
Plugins die meelifen
Elke plugin die hookt op init of rest_api_init voegt gewicht toe aan elk REST-request. SEO-plugins, analytics-integraties, security-plugins - ze draaien allemaal mee, zelfs als je endpoint niks met hun functionaliteit te maken heeft.
Inventariseer welke callbacks op rest_pre_dispatch, rest_request_before_callbacks en rest_post_dispatch hangen. Via debugging van trage plugins vind je de zwaargewichten. Vaak kun je een plugin deactiveren op REST-context:
add_action('plugins_loaded', function () {
if (defined('REST_REQUEST') && REST_REQUEST) {
remove_action('init', 'zware_plugin_init');
}
}, 1);
Authenticatie en rate limiting
Cookie-authenticatie met nonces is handig voor de block editor maar zwaar voor high-volume API-gebruik. Voor externe integraties zijn application passwords of JWT met een korte geldigheid sneller, vooral omdat je geen cookie-check en nonce-verificatie per request doet.
Rate limiting is geen directe performance-winst, maar voorkomt dat één misbruikte client je hele site op de knieën krijgt. Implementeer het op NGINX-niveau met limit_req_zone of voor de REST API specifiek via een must-use plugin die per IP of API-key telt.
HTTP-niveau optimalisaties
Vergeet de transportlaag niet. HTTP/2 of HTTP/3 maakt parallelle requests van een headless frontend merkbaar sneller. Zie WordPress en HTTP/3: wat levert het op? voor de achtergrond.
Stuur daarnaast de juiste headers:
Cache-Control: public, max-age=300voor publieke dataETagvoor conditional requests (304 Not Modified)Vary: Accept-Encoding, Authorizationzodat caches correct segmenteren- Gzip of Brotli-compressie op JSON-responses (kan tot 80% besparen)
De officiële WordPress REST API Handbook bevat ook aanbevelingen voor caching-headers die de moeite waard zijn om door te nemen.
Alternatieven overwegen
Als je alle bovenstaande stappen hebt doorlopen en de REST API nog steeds niet performt zoals je wilt, kijk dan naar alternatieven:
- WPGraphQL voor use cases met veel nested data of waar overfetching een probleem is
- Custom endpoints die
WP_Queryoverslaan en direct via$wpdbwerken - Headless architectuur met een aparte API-laag (bijvoorbeeld Laravel of Node.js die data leest uit de WordPress-database), zoals besproken in Headless WordPress: wanneer en waarom
- Static generation: genereer JSON-files bij publicatie en serveer die direct vanuit NGINX
De Google Core Web Vitals documentatie benadrukt dat API-responstijd direct impact heeft op perceived performance van je frontend. Elke 100ms telt.
Veelgestelde vragen
Waarom is de WordPress REST API soms traag?
De REST API laadt standaard de volledige WordPress-bootstrap per request. Dat betekent alle plugins, thema-functies en hooks worden geladen, zelfs voor een simpele JSON-response. Combineer dat met zware queries en je hebt responstijden van honderden milliseconden.
Kun je de REST API cachen?
Ja, dat kan prima. Je gebruikt hiervoor bijvoorbeeld een object cache zoals Redis voor losse responses, of een full page cache op endpoint-niveau. Let op dat je authenticated requests niet per ongeluk cached en dat je cache invalidation goed regelt.
Is GraphQL sneller dan de REST API in WordPress?
Niet automatisch. GraphQL kan efficiënter zijn omdat je precies de velden opvraagt die je nodig hebt, maar de onderliggende WordPress-bootstrap is hetzelfde. Voor sommige use cases met veel nested data is WPGraphQL significant sneller, voor simpele lijsten vaak niet.
Hoe voorkom ik dat plugins mijn REST API vertragen?
Veel plugins registreren onnodig hooks op REST-requests. Gebruik Query Monitor om te zien welke plugin de meeste tijd kost en unhook onnodige callbacks in een must-use plugin. Overweeg ook een aparte PHP-FPM pool voor REST-verkeer.
Moet ik de REST API uitschakelen als ik hem niet gebruik?
Helemaal uitschakelen is zelden verstandig - de block editor en veel core-features leunen erop. Je kunt wel publieke endpoints afschermen met authenticatie, rate limiting toepassen en ongebruikte routes deregistreren om de aanvalsoppervlakte te verkleinen.