REST API performance in WordPress optimaliseren

Leer hoe je de WordPress REST API sneller maakt. Praktische tips voor caching, queries en authenticatie voor betere performance en snellere responses.

23 april 20268 min leestijdDoor We Develop Communication

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:

  1. NGINX of Apache routeert het request naar PHP-FPM
  2. WordPress laadt de volledige bootstrap
  3. De REST controller matcht de route en parsed parameters
  4. WP_Query wordt uitgevoerd met de opgegeven filters
  5. Voor elke post wordt metadata, auteur en featured image opgehaald
  6. prepare_item_for_response transformeert elke post naar JSON
  7. 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
  • SAVEQUERIES in wp-config.php om queries te loggen via $wpdb->queries
  • Custom X-Request-Time headers 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_rows via de rest_post_query filter als je geen paginering nodig hebt - dat scheelt een SQL_CALC_FOUND_ROWS query
  • Gebruik fields om alleen velden op te halen die je nodig hebt
  • Vermijd meta_query op grote datasets zonder een proper geïndexeerde meta_key
  • Overweeg een _embed parameter 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=300 voor publieke data
  • ETag voor conditional requests (304 Not Modified)
  • Vary: Accept-Encoding, Authorization zodat 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_Query overslaan en direct via $wpdb werken
  • 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.

Veelgestelde vragen

Klaar om digitaal te groeien?

Wij helpen Nederlandse bedrijven met webtechnologie en SEO-strategieën die écht werken. Neem vrijblijvend contact op.