503 Service Unavailable in WordPress

Een 503 Service Unavailable betekent dat de server voor je WordPress-site het verzoek nu even niet kan afhandelen, bijna altijd omdat iets overbelast is, gerate-limit wordt of in onderhoud staat. Dit artikel legt het mechanisme uit, behandelt de vijf echte oorzaken, helpt je ze van elkaar te onderscheiden en geeft per oorzaak de fix.

Je WordPress-site toont een lege pagina met de tekst "503 Service Unavailable", of Chrome meldt "This page isn't working: HTTP ERROR 503". De response-inspector laat statuscode 503 zien. De fout komt terug bij bezoekers en bij jou, in elke browser, op mobiel en op desktop. Soms verdwijnt hij na een refresh, soms blijft hij hangen voor de duur van een verkeerspiek.

Wat een 503 echt betekent

RFC 9110 §15.6.4 definieert 503 als: "the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay." Het sleutelwoord is unable. Niet gecrasht. Niet traag. Niet kapotte upstream. De server leeft en wil best praten, hij weigert alleen dit specifieke verzoek nu af te handelen en verwacht later weer beschikbaar te zijn. Diezelfde sectie voegt toe dat de server een Retry-After-header MAG meesturen om aan te geven wanneer de client mag terugkomen.

Die definitie is belangrijk omdat hij je vertelt wat een 503 niet is. Een 503 is niet de applicatie die crasht, en het is niet de proxy die het opgeeft op een trage upstream. Het is een bewust "nee" vanuit ergens in je stack. Het werk van diagnose is uitzoeken welk deel van de stack nee zei, en waarom.

Een 503 is niet hetzelfde als de andere 5xx-fouten in deze categorie, en het verschil is belangrijk voor je diagnose:

  • 503 Service Unavailable: de server weigert dit verzoek nu af te handelen. Overbelasting, rate limit of onderhoud. Hierover gaat dit artikel.
  • 502 Bad Gateway: de upstream gaf een ongeldig of leeg antwoord, meestal omdat de worker midden in het verzoek crashte.
  • 504 Gateway Timeout: de upstream was bereikbaar maar duurde te lang. PHP leeft nog, het is gewoon traag.
  • 500 Internal Server Error: de applicatie zelf is netjes vastgelopen en gaf een 500 terug aan de proxy.

Kortom: een 502 is een lijk, een 504 is stilte, een 500 is een bekentenis en een 503 is een "ga weg".

Eén specifieke WordPress-503 hoort hier vooraan genoemd te worden. Zegt de response body letterlijk "Briefly unavailable for scheduled maintenance. Check back in a minute.", dan komt die 503 uit WordPress zelf tijdens een update, niet doordat je server overbelast is. WordPress's wp_maintenance() stuurt dat bericht met statuscode 503 en een Retry-After: 600-header zolang er een .maintenance-bestand in de site-root staat. De fix daarvoor staat in mijn artikel over de WordPress-onderhoudsmelding. De rest van dit artikel gaat over de generieke server-side 503.

Veelvoorkomende oorzaken, op volgorde van waarschijnlijkheid

1. De server is overbelast (CPU, RAM of een verzadigde PHP-FPM-pool)

Dit is de oorzaak achter de meeste 503's op een drukke site. Het PHP-FPM-plafond zit in pm.max_children. Zodra elke worker bezig is en de listen-backlog ook vol is, stopt FPM met netjes verbindingen aannemen en geeft de webserver vooraan een 503 terug aan welk verzoek dan ook pech had. Sommige hostingpanelen geven zelf een 503 terug zodra hun per-account CPU- of RAM-cap is geraakt, voordat het verzoek PHP überhaupt bereikt. Hoe dan ook ziet het er voor de bezoeker hetzelfde uit en is de onderliggende oorzaak hetzelfde: de bak heeft op dit moment geen resources om het verzoek aan te nemen. Dit faalpatroon beschrijf ik uitgebreid in mijn artikel over PHP workers.

2. nginx (of een WAF) rate-limit de client

nginx heeft twee rate-limit-modules. De ngx_http_limit_req_module limiteert verzoeken per seconde per key, en de ngx_http_limit_conn_module limiteert gelijktijdige connecties per key. Beide geven standaard een 503 terug zodra de limiet wordt geraakt (limit_req_status 503 en limit_conn_status 503 — zie 429 Too Many Requests in WordPress voor de correcte rate-limit-semantiek —). Veel managed hosts shippen behoorlijk strakke defaults om de gedeelde server te beschermen, en een WAF voor nginx (Wordfence, Cloudflare rate limiting rules, AWS WAF) doet hetzelfde. Een burst vanuit één IP, een uptime-monitor die te enthousiast pollt of je eigen WP-Cron die een endpoint platslaat kan ieder afzonderlijk deze limieten triggeren en een 503 produceren die alleen die ene client raakt. De rest van de site werkt prima, en juist dat is het signaal.

3. De host of jijzelf zette de site in onderhoud

Een gepland onderhoudsvenster, een deploy-script of een handmatige "503-pagina" die de host serveert tijdens een interventie geven allemaal opzettelijk een 503 terug. Sommige hosts geven ook een 503 nadat ze je site hebben verplaatst of stilgelegd om factuur-redenen. Vanaf de bezoekerskant is dit niet te onderscheiden van het overload-scenario, maar de oorzaak is bewust en de fix is "wachten of de host bellen". Hier hoort ook de WordPress-specifieke onderhoudsmelding die ik hierboven al noemde.

4. De applicatie geeft expres een 503 terug

Deze komt zelden voor, maar het is goed om te weten. Een plugin of custom code kan status_header( 503 ) (of vergelijkbaar) aanroepen tijdens een installatiestap, op een specifiek endpoint of terwijl een lange achtergrondtaak loopt. WooCommerce-plugins geven soms een 503 tijdens checkout als een payment gateway niet bereikbaar is. Een staging-mode-plugin antwoordt soms met 503 op niet-toegestane IP's. Het kenmerk van deze oorzaak is dat de 503 consistent is voor één URL of één bezoeker, terwijl de rest van de site gewoon werkt. Het nginx access log laat dan zien dat de 503 vanuit PHP-FPM terugkomt (upstream-status 503), niet vanuit nginx zelf.

5. De origin achter een CDN is onbereikbaar en de CDN serveert een 503

Gebruik je Cloudflare of een andere CDN en ligt je origin er echt uit, dan kan de CDN de upstream niet bereiken en serveert hij zijn eigen 503 (of soms zijn eigen 502, afhankelijk van het faalpatroon). Cloudflare-branded fouten zijn makkelijk te herkennen: in de paginakop staat "Cloudflare". Dit komt minder vaak voor dan oorzaken 1 tot 3, maar ik zie het na een deploy die het origin-IP wisselt zonder DNS of de Cloudflare origin rule bij te werken, of als een origin-firewall ineens de IP-ranges van Cloudflare blokkeert.

Stel vast welke oorzaak het is

Deze checks zijn niet-destructief. Doe ze in volgorde, voordat je ook maar één configwaarde aanpast.

Check 1: lees het access log. Dit is veruit de belangrijkste stap. Open de access-logviewer in je hostingpaneel (meestal onder "Logs", "Toegangslogs" of "Statistieken"). Heb je SSH-toegang, dan staat het ruwe bestand doorgaans op /var/log/nginx/access.log. Voor een 503 wil je de response code en het upstream_status-veld naast elkaar zien. Een regel als:

"GET / HTTP/1.1" 503 - "..." "..." rt=0.000 uct=- urt=-

zonder upstream-responstijd betekent dat nginx zelf de 503 maakte, en dat is oorzaak #2 (rate limit) of oorzaak #3 (onderhoud op de front-laag). Een regel als:

"GET / HTTP/1.1" 503 - "..." "..." rt=0.342 uct=0.000 urt=0.341 us=503

met een echte urt en us=503 betekent dat PHP-FPM (of een app daarachter) zelf de 503 stuurde, wat oorzaak #1 (FPM verzadigd of WordPress zelf) of oorzaak #4 (app stuurt expres een 503) is.

Je weet dat het werkt als: je de exacte access-log-regel kunt citeren voor het mislukte verzoek, je het tijdstip kunt koppelen aan een bezoekersmelding, en je hebt besloten of de 503 van nginx of van de upstream kwam.

Check 2: lees het error log. De meeste hostingpanelen hebben een foutlogviewer naast de access-logviewer. Kijk daar eerst. Heb je SSH-toegang, dan staat het ruwe bestand doorgaans op /var/log/nginx/error.log. Voor een 503 vanuit oorzaak #2 zie je:

limiting requests, excess: 1.234 by zone "one", client: 1.2.3.4, server: jouwsite.nl, request: "GET / HTTP/1.1"

Dat is limit_req die een verzoek afwijst. Of:

limiting connections by zone "addr", client: 1.2.3.4, server: jouwsite.nl

Dat is limit_conn. Voor oorzaak #1 (FPM-pool vol) zie je:

WARNING: [pool www] server reached pm.max_children setting (10), consider raising it

in het PHP-FPM error log (in sommige hostingpanelen zichtbaar onder "PHP-logs"; heb je SSH-toegang, kijk dan in /var/log/php8.3-fpm.log), en het nginx error log laat rondom datzelfde tijdstip timeouts of connection failures zien. Die regel en wat je eraan moet doen, staat in mijn artikel over PHP workers.

Je weet dat het werkt als: je een logregel hebt die de oorzaak benoemt: limiting requests, limiting connections of pm.max_children. Verschijnt geen van die drie, dan komt de 503 uit een laag die jij niet direct beheert (oorzaak #3 of #5).

Check 3: bekijk het resourcegebruik tijdens het incident. Open de resourcegrafieken in je hostingpaneel (CPU, RAM en PHP-workers of "processen" als die worden getoond). Staat CPU of RAM vastgepind op 100% op het moment dat de 503 verscheen, dan zit je in oorzaak #1. Veel managed hosts tonen een "PHP workers"- of "actieve processen"-grafiek die je hetzelfde vertelt zonder dat je SSH nodig hebt.

Heb je SSH-toegang: draai ps -ef | grep 'php-fpm: pool' | grep -v grep | wc -l op de server. Vergelijk dat aantal met de waarde van pm.max_children in je poolconfig (meestal /etc/php/8.3/fpm/pool.d/www.conf). Als de actieve workers gelijk zijn aan pm.max_children, zit je in oorzaak #1. Zit je ruim onder het plafond, dan is de pool niet de bottleneck en komt de 503 niet uit FPM.

Je weet dat het werkt als: je kunt vaststellen of de resources volliepen of nog ruimte hadden op het moment van de 503.

Check 4: omzeil de CDN. Gebruik je Cloudflare of een vergelijkbare edge proxy, kijk dan of de 503-pagina gebranded is met het logo of de huisstijl van de CDN. Een Cloudflare-branded 503-pagina is onmiskenbaar en vertelt je dat het probleem op de edge zit, niet bij je origin. Je kunt ook Cloudflare even pauzeren vanuit het Cloudflare-dashboard om te zien of de site direct laadt.

Heb je SSH-toegang of een terminal: hit dan direct het origin-IP met curl --resolve jouwsite.nl:443:1.2.3.4 https://jouwsite.nl/ en time de respons. Geeft de origin een nette 200 terug en alleen de edge-URL een 503, dan ligt het probleem op de edge: oorzaak #5. Geeft de origin ook een 503, dan zit het in je eigen stack en zit je terug bij oorzaken #1 tot #4.

Je weet dat het werkt als: je met zekerheid kunt vaststellen of het probleem op de CDN-edge of bij je origin-server zit.

Oplossingen, per oorzaak

Fix voor oorzaak #1: de server is overbelast

De juiste fix is zelden "meer workers". Workers toevoegen aan een verzadigde pool verschuift het probleem meestal van een 503 naar een trage site, omdat de workers worden vastgehouden door dezelfde trage query, externe API-call of ontspoorde plugin. Lees het aparte artikel over PHP workers voor de structurele fix. De korte versie:

  1. Zoek uit wat de workers vasthoudt (PHP-FPM slowlog plus een proces-snapshot tijdens het incident).
  2. Cache die pagina of dat endpoint op de edge zodat het PHP helemaal niet meer raakt.
  3. Verplaats lange jobs uit de request lifecycle naar Action Scheduler of WP-CLI.
  4. Pas dáárna verhoog je pm.max_children, en alleen als je CPU- en RAM-headroom dat toelaat.

Zit de overload op het niveau van je hostingaccount (per-account CPU- of RAM-cap, wat het paneel van je host je ook netjes laat zien), dan geldt dezelfde logica: meer cachen, minder doen per request, en pas je plan resizen als je hebt aangetoond dat de workload zelf de juiste maat heeft.

Verificatie: de actieve worker-count tijdens piekmomenten zit duidelijk onder pm.max_children, de resourcegrafieken van je host zitten niet vastgepind op 100%, en dezelfde URL die eerder 503 gaf, geeft nu een 200 in het access log.

Fix voor oorzaak #2: er ging een rate limit af

Beslis eerst of de limit zijn werk doet. Komt het bron-IP in het error log van een echte bot die je niet wil (een agressieve scraper, een credential-stuffing crawl), dan klopt de 503 en is de fix om er vanaf te blijven en de regel eventueel aan te scherpen. Komt het bron-IP van je eigen monitor, je eigen WP-Cron of een echte bezoeker die in een te strakke default hangt, dan klopt de regel niet bij je verkeer.

Zit je rate limit op een Cloudflare WAF-regel of een andere edge-level rate limit, pas hem dan direct aan in het Cloudflare-dashboard onder Security > WAF > Rate limiting rules. Komt de rate limit van de nginx-configuratie op je hostingserver, neem dan contact op met de support van je host en vraag of ze de limit_req burst-toeslag voor je site willen verhogen, of het IP dat geblokkeerd wordt willen whitelisten. Bij managed hosting is dit bijna altijd een supportticket, omdat je doorgaans geen directe toegang hebt tot nginx-configbestanden.

Heb je SSH-toegang en beheer je je eigen nginx-configuratie, verhoog dan de limit_req-regel door de bijbehorende zone aan te passen:

limit_req_zone $binary_remote_addr zone=one:10m rate=20r/s;

server {
    location / {
        limit_req zone=one burst=40 nodelay;
        # ... rest van de location ...
    }
}

rate is wat je continu toelaat, burst is hoe groot de wachtrij mag zijn en nodelay zorgt dat een burst meteen door mag in plaats van druppelsgewijs.

Verificatie: dezelfde client die 503's kreeg krijgt nu een 200, het error log bevat geen limiting requests-regels meer voor dat bron-IP, en de bots die je écht wilde tegenhouden worden nog steeds tegengehouden.

Fix voor oorzaak #3: de host of jijzelf zette de site in onderhoud

Heb jij hem in onderhoud gezet, dan haal je hem er ook uit: verwijder de onderhoudspagina, beëindig de deploy of unpause de site. Zette de host hem in onderhoud, dan staat het antwoord op zijn statuspagina of in een supportticket en valt er in WordPress zelf niets te fixen. Is de onderhoudsmelding de WordPress-specifieke tekst "Briefly unavailable for scheduled maintenance", dan staat de fix in mijn artikel over precies die melding, want dan moet er een specifiek .maintenance-bestand uit de site-root.

Verificatie: de onderhoudspagina is weg, de response-code is geen 503 meer, en een normale pagina rendert.

Fix voor oorzaak #4: de applicatie stuurt expres een 503

Zoek uit welke plugin of welk thema verantwoordelijk is. Zet WP-debug logging aan door wp-config.php te openen via de bestandsbeheerder van je hostingpaneel (of via SFTP) en de volgende regels toe te voegen voor het /* That's all, stop editing! */-commentaar:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Reproduceer de 503 en download wp-content/debug.log via de bestandsbeheerder. Kijk naar warnings rond datzelfde tijdstip. Komt daar niets bruikbaars uit, deactiveer dan plugins één voor één vanuit wp-admin onder Plugins > Geïnstalleerde plugins, totdat de 503 stopt. Kun je wp-admin niet bereiken vanwege de 503, gebruik dan de bestandsbeheerder om individuele pluginmappen in wp-content/plugins/ te hernoemen (bijvoorbeeld problematic-plugin naar problematic-plugin.disabled) zodat WordPress terugvalt op geen plugin. De plugin die je net uitschakelde is degene die status_header( 503 ) aanroept. Update hem, zet hem goed in of vervang hem. Tijdelijk overschakelen naar een default theme zoals Twenty Twenty-Four via Weergave > Thema's is dezelfde truc voor de themalaag.

Zet WP_DEBUG na het troubleshooten weer terug op false in wp-config.php zodat er geen debugoutput in productie blijft staan.

Verificatie: de URL die consistent een 503 gaf, geeft nu een 200, en geen enkele plugin in je stack stuurt nog opzettelijk een 503 voor normaal verkeer.

Fix voor oorzaak #5: de CDN bereikt de origin niet

Als de 503 een Cloudflare-logo (of dat van je andere CDN) heeft, zit het probleem op de edge. Bevestig met curl --resolve dat de origin gezond is. Is dat zo, dan praat de CDN met het verkeerde IP, dropt een origin-firewall CDN-verkeer, of wijst een Cloudflare origin rule naar een host die niet meer bestaat. Werk het DNS A-record of de origin rule bij naar het huidige IP, en bevestig dat de origin-firewall de IP-ranges van Cloudflare toelaat. Ligt de origin echt plat, fix dan eerst de origin (oorzaken 1 tot 4) en de edge volgt vanzelf.

Verificatie: dezelfde URL die eerst via de edge een 503 gaf, geeft nu via de edge een 200, en het origin access log laat een matchende 200 zien zonder upstream-fouten.

Wanneer je het moet escaleren

Lukt het je binnen 30 minuten niet om met de stappen hierboven de oorzaak te pinpointen, dan geef je het door aan je host of developer. Houd onderstaande lijst klaar, want dat is precies het eerste wat ze gaan vragen:

  • De exacte URL of admin-actie die de 503 triggert.
  • Het tijdstip van de fout, met tijdzone, en of het reproduceerbaar is of alleen sporadisch.
  • Je hostingtier en stack (shared, VPS, managed, container; nginx of Apache; PHP-versie).
  • De matchende regel uit het nginx access log, inclusief het upstream status-veld als dat erbij staat.
  • De matchende regel uit het nginx error log, vooral alles met limiting requests, limiting connections of pm.max_children.
  • De actieve worker-count op het moment van de 503, ten opzichte van de geconfigureerde pm.max_children.
  • Of de 503 op elke URL valt of alleen op specifieke endpoints, en of het elke bezoeker treft of alleen specifieke bron-IP's.
  • Of de site achter Cloudflare of een andere edge proxy zit, en of de 503 ook optreedt als je die omzeilt.
  • De lijst met actieve plugins op de site, vooral plugins die in de afgelopen 48 uur zijn geüpdatet.

Stuur dat in het eerste bericht. Het scheelt een hele heen-en-weer-ronde en routeert het ticket meteen naar de juiste engineer.

Hoe je voorkomt dat het terugkomt

Een hardnekkige 503 is bijna altijd een sizing- of rate-limit-probleem dat zich voordoet als een serverfout. Drie dingen houden de fout zeldzaam op een gezonde site:

  • Cache stevig op de edge. Een verzoek dat uit een full-page cache komt, raakt PHP-FPM helemaal niet en kan de pool dus ook niet verzadigen. Sites die 95% van hun verkeer uit cache serveren overleven met kleine pools. Sites waar elk verzoek dynamisch is (membership-sites, grote WooCommerce-stores met ingelogde klanten) hebben ofwel grotere pools nodig of kortere requests.
  • Stem rate limits af op je echte verkeer. Default limit_req-regels en Cloudflare WAF-defaults zijn ontworpen om een server te beschermen tegen anonieme misbruik, niet tegen je eigen monitor of je eigen cron. Whitelist het IP van je monitor, je kantoor-IP en de IP's van services die legitiem met je API praten. De truc is rate limits te hebben die bots bijten en jou nooit.
  • Monitor de FPM-pool, niet alleen CPU. Hosts die "alles groen" tonen op CPU-dashboards kunnen toch 503's serveren omdat PHP-workers staan te wachten op een database, niet op CPU. Houd de actieve worker-count en het FPM error log in de gaten op pm.max_children-waarschuwingen. Worker-saturatie is het echte signaal en het verschijnt eerder dan de 503.

Antwoordt een single request op je site cold binnen één seconde zonder edge cache, en gaat de worker daarna heelhuids terug de pool in, dan zou een 503 onder normale omstandigheden onmogelijk moeten zijn. Alles daarboven is het systeem dat je vertelt dat de workload te groot is voor de huidige pool, dat de rate limit te strak staat voor het huidige verkeer, of dat iets bezoekers expres terugstuurt om later terug te komen.

Wil je dat dit niet steeds jouw probleem is?

Als storingen blijven terugkomen, is de 'fix' vaak consistent beheer: updates, backups en monitoring die niet versloffen.

Bekijk WordPress onderhoud

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.