Skip to content
Terug naar blog
Tutorials

HTTP-statuscodes spiekbriefje: 1xx-5xx volledig uitgelegd

Volledige HTTP-statuscodes referentie van 1xx tot 5xx met voorbeelden, veelgemaakte fouten (401 vs 403, 301 vs 302) en SEO-impact. Bekijk het spiekbriefje.

14 min leestijd

HTTP-statuscodes spiekbriefje: 1xx-5xx uitgelegd

Je opent DevTools en het tabblad Netwerk staat half in het rood. Je endpoint geeft 502 in productie, 200 lokaal, en een collega vraagt net op Slack: “moet dit een 401 of een 403 zijn?”. HTTP-statuscodes lijken simpel (drie cijfers, vijf categorieën), maar de verkeerde keuze lekt informatie, breekt SEO en maakt on-call diensten ellendig.

Deze gids is een HTTP-statuscodes spiekbriefje voor werkende ontwikkelaars. Je krijgt drie dingen: (1) een snelle referentietabel met elke code die je in de praktijk tegenkomt, (2) beslismatrices voor de paren die mensen door elkaar halen (301 vs 302, 401 vs 403, 404 vs 410, 502 vs 504), en (3) een tooling-sectie die laat zien hoe je statuscodes inspecteert vanuit curl, fetch en Python requests. Elke code hieronder is gebaseerd op RFC 9110, de huidige standaard voor HTTP-semantiek, en het IANA HTTP Status Code Registry.

Snelle referentie: alle HTTP-statuscodes in één oogopslag

Hier zijn de codes die je in productie tegenkomt, gegroepeerd per klasse. Bookmark deze tabel; de rest van het artikel legt de lastigere uit.

CodeNaamWanneer je hem ziet
100ContinueEen grote POST-body verzenden met Expect: 100-continue
101Switching ProtocolsWebSocket-handshake, HTTP/2-upgrade
103Early HintsServer pusht Link-headers vóór het echte antwoord
200OKStandaard succes voor GET, PUT, PATCH
201CreatedPOST die een resource aanmaakt (geeft Location terug)
202AcceptedAsynchrone job in de wachtrij, werk nog niet klaar
204No ContentDELETE-succes, PUT zonder body in het antwoord
206Partial ContentRange-request, video-seek, hervatbare download
301Moved PermanentlyOude URL gepensioneerd, zoekmachines dragen link equity over
302FoundTijdelijke redirect, originele URL blijft canoniek
303See OtherPost/Redirect/Get-patroon na een form-POST
304Not ModifiedConditionele GET met passende ETag of If-Modified-Since
307Temporary RedirectNet als 302, maar methode en body blijven behouden
308Permanent RedirectNet als 301, maar methode en body blijven behouden
400Bad RequestOngeldige JSON, ontbrekend verplicht veld, schema-fout
401UnauthorizedGeen credentials of verlopen token
403ForbiddenGeauthenticeerd maar niet toegestaan
404Not FoundResource bestaat niet (of je verbergt hem)
405Method Not AllowedPOST naar een GET-only endpoint (moet Allow bevatten)
408Request TimeoutClient deed te lang over het verzenden van het verzoek
409ConflictOptimistic-lock fout, dubbele sleutel
410GoneResource permanent verwijderd, komt niet terug
415Unsupported Media TypeVerkeerd Content-Type, bv. XML naar een JSON-API
422Unprocessable ContentSyntaxis geldig, semantiek ongeldig (validatiefout)
425Too EarlyTLS 1.3 early-data replay-risico
428Precondition RequiredServer vereist If-Match om verloren updates te voorkomen
429Too Many RequestsRate limited (moet Retry-After bevatten)
451Unavailable for Legal ReasonsDMCA, AVG-verwijdering, geo-blokkade
500Internal Server ErrorOnafgehandelde exception in je code
501Not ImplementedMethode of feature niet ondersteund (zelden in REST)
502Bad GatewayUpstream gaf een ongeldig antwoord terug
503Service UnavailableOnderhoudsmodus of overbelasting
504Gateway TimeoutUpstream antwoordde niet op tijd
507Insufficient StorageWebDAV is door zijn schijfruimte heen
508Loop DetectedOneindige redirect of recursie in WebDAV
511Network Authentication RequiredCaptive portal op hotel- of luchthaven-WiFi

De rest van dit artikel ontleedt elke klasse met beslismatrices, anti-patronen en de SEO-gevolgen wanneer je het verkeerd doet.

Hoe HTTP-statuscodes werken (anatomie van 3 cijfers)

Waarom drie cijfers?

HTTP-statuscodes zijn drie decimale cijfers omdat HTTP/0.9 een signaal van vaste breedte nodig had: klein genoeg zodat een parser er snel op kan vertakken, en groot genoeg om ruimte te laten voor nieuwe codes. Drie cijfers geven 900 mogelijke waarden (100-999), wat ruim voldoende is. Het IANA-register gebruikt er vandaag slechts ongeveer 60 van.

Het eerste cijfer is de klasse. Het tweede en derde zijn de specifieke code binnen die klasse. Een client die 418 niet herkent, moet terugvallen op de afhandeling als een generieke 4xx. RFC 9110 §15 maakt dit expliciet: clients moeten onbekende codes behandelen als de x00 van hun klasse.

De vijf categorieën in één oogopslag

KlasseBetekenisBody verplicht?Standaard cachebaar?
1xxInformatief: tussentijds, er komt meerNeeNee
2xxSucces: request begrepen en geaccepteerdVaakHangt af van de methode
3xxRedirect: verdere actie nodigOptioneel301, 308 ja; 302, 307 nee
4xxClientfout: jouw fout, fix het verzoekJa (uitleggen)Doorgaans nee
5xxServerfout: onze fout, opnieuw proberen kan helpenJa (uitleggen)Nee

De kolom “standaard cachebaar” is belangrijk. CDN’s en browsers cachen 301 en 308 agressief en voorgoed. De verkeerde redirect-code kiezen in productie is moeilijk terug te draaien omdat gebruikers de redirect lokaal in de cache hebben staan. We komen hier terug op in de SEO-sectie.

Wil je dieper in de URL-structuur kijken (waar de redirect-codes op werken), dan behandelt de URL-encoderen en -decoderen percent-encoding, query strings en de byte-pijplijn die bepaalt wat een URL überhaupt geldig maakt.

1xx: informatief (wanneer je ze echt ziet)

De meeste ontwikkelaars zien jarenlang geen 1xx rechtstreeks. Het zijn tussentijdse antwoorden waarmee de server de client vertelt “ik ben er nog, ga door.” Browser-DevTools verbergt ze meestal, en de meeste HTTP-libraries vouwen ze op in het uiteindelijke antwoord.

Voor elke code hieronder is MDN’s HTTP response status reference de meest toegankelijke kruisreferentie als je een tweede paar ogen op een definitie wilt.

100 Continue

De client stuurt Expect: 100-continue in zijn headers en wacht voordat hij een grote request-body verstuurt. De server antwoordt met 100 Continue als hij de body wil accepteren, of met een 4xx als hij het verzoek toch gaat afwijzen. Dat scheelt bandbreedte bij grote uploads. Er is geen reden om 200 MB te versturen als de server het toch afwijst vanwege een ontbrekende header.

curl -v -H "Expect: 100-continue" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @big-file.bin \
  https://api.example.com/upload

Zie je < HTTP/1.1 100 Continue niet in de verbose output, dan heeft je client de header waarschijnlijk eraf gestript of de server ondersteunt het niet.

101 Switching Protocols

De handshake die een HTTP-verbinding omzet naar een WebSocket- of HTTP/2-verbinding. De client stuurt Upgrade: websocket, de server antwoordt met 101 Switching Protocols, en vanaf dat moment spreekt de verbinding een ander protocol. Je ziet dit in het Netwerk-tabblad van elke chat-app, live dashboard of samenwerkingstool.

103 Early Hints

Een relatief nieuwe code (RFC 8297, 2017) waarmee de server Link-headers voor preload-hints kan sturen voordat het hoofdantwoord klaar is. De browser begint CSS en JS op te halen terwijl de server nog aan het renderen is. In 2026 ondersteunen Cloudflare, Fastly en Vercel allemaal 103 in productie. Het is het moderne alternatief voor HTTP/2 server push, die in Chrome is uitgefaseerd.

HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script

HTTP/1.1 200 OK
Content-Type: text/html
...

Anti-patroon-check. Ziet je client nooit 1xx-codes wanneer je ze verwacht, dan ligt het probleem meestal bij een reverse proxy. Oudere nginx-versies strippen Expect: 100-continue en 103 Early Hints. Controleer je proxyconfig voordat je aanneemt dat de server stuk is.

2xx: succes (verder dan alleen 200)

200 OK voor alles teruggeven is de meest voorkomende code-smell in REST-API’s. De 2xx-familie draagt semantische informatie die clients slimmer en caches efficiënter maakt.

200 OK

De standaard. Een GET geeft de resource terug, een PUT geeft de bijgewerkte resource terug (of 204), een PATCH geeft de gepatchte resource terug. Heb je geen reden om een specifiekere code te gebruiken, gebruik dan 200.

201 Created

Een POST die een nieuwe resource aanmaakt moet 201 plus een Location-header teruggeven die naar de nieuwe resource wijst. Zo ontdekken RESTful clients de canonieke URL van het ding dat ze net hebben gemaakt.

HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json

{"id": 42, "name": "Ada Lovelace"}

202 Accepted

De server heeft het verzoek geaccepteerd, maar is nog niet klaar met verwerken. Gebruik dit voor asynchroon werk; de client moet pollen, op een webhook abonneren of een status-endpoint controleren. Combineer het met een job-ID in de body.

204 No Content

Succes, geen body. Veel gezien bij DELETE (de resource is weg, wat zou je teruggeven?) en bij PUT-operaties waar de client de nieuwe staat al kent. Browsers veranderen de huidige pagina niet als een formulierverzending 204 teruggeeft, handig voor fire-and-forget acties in single-page apps.

206 Partial Content

Wordt teruggegeven voor range-requests: de client vroeg om bytes 1000-2000 met een Range: bytes=1000-2000-header, en de server reageerde met precies dat stuk. Video-streaming, hervatbare downloads en HTTP-gebaseerde file-syncing bouwen allemaal op 206.

Beslissing: 200 vs 201 vs 204 voor POST

ScenarioCodeBody
POST maakt een nieuwe resource201 CreatedNieuwe resource (of alleen ID) + Location
POST start asynchroon werk, resultaat nog niet klaar202 AcceptedJob-ID, poll-URL
POST is een actie zonder resource (bv. /login)200 OKActieresultaat (token, status)
POST slaagt maar antwoord is leeg204 No Content(geen)

Kun je niet kiezen tussen 200 en 201, vraag jezelf dan af: “heeft de server een resource gemaakt die nu zijn eigen URL heeft?” Zo ja, 201. Zo nee, 200.

3xx: redirects (301 vs 302 vs 307 vs 308)

Redirects zijn de meest misbruikte klasse. Het verschil tussen 301, 302, 307 en 308 komt neer op drie onafhankelijke vragen: is de verhuizing permanent, blijft de methode behouden, en is het antwoord cachebaar.

301 Moved Permanently

De resource is verhuisd en komt niet terug. Zoekmachines dragen link equity over naar de nieuwe URL. Browsers en CDN’s cachen 301 voor onbepaalde tijd. Redirect je /old naar /new met een 301 en verander je later van gedachten, dan blijven gebruikers met gecachete redirects voor altijd naar /new gaan (of totdat ze hun cache leegmaken).

Historisch gezien kunnen browsers de request-methode bij een 301 herschrijven (POST naar GET). HTTP/1.1 introduceerde daarom 308 om dat te repareren.

302 Found

Tijdelijke redirect. De originele URL is nog steeds canoniek; zoekmachines moeten het origineel blijven indexeren. Gebruik dit voor A/B-test-routing, onderhoudspagina’s of “log in om door te gaan”-flows.

Net als 301 herschreven browsers historisch POST naar GET bij 302. Moet je een POST redirecten en POST houden, gebruik dan 307 in plaats daarvan.

303 See Other

Herschrijft altijd de methode naar GET. Het Post/Redirect/Get-patroon: formulier post naar /submit, server geeft 303 terug met Location: /thank-you, browser doet een GET /thank-you. De thank-you-pagina vernieuwen verstuurt het formulier niet opnieuw. Daar is 303 voor ontworpen.

304 Not Modified

Het conditionele antwoord. Client stuurt If-None-Match: "abc123" (of If-Modified-Since), server controleert of de resource is veranderd, en zo niet geeft 304 terug zonder body. De browser gebruikt zijn gecachete kopie. Zo houdt elk CDN en elke caching-laag je site snel.

307 Temporary Redirect

Net als 302, maar de methode mag niet veranderen. POST blijft POST, body blijft behouden. Gebruik dit als je een tijdelijke redirect wilt op een niet-GET-verzoek.

308 Permanent Redirect

Net als 301, maar de methode mag niet veranderen. De moderne, veiligere keuze voor permanente redirects op API’s die POST/PUT accepteren.

Beslismatrix: welke redirect-code?

Permanent (cache voor altijd)Tijdelijk (niet cachen)
Methode mag veranderen naar GET301 Moved Permanently302 Found
Methode moet hetzelfde blijven308 Permanent Redirect307 Temporary Redirect

Bijzonder geval: wil je specifiek POST → GET (het Post/Redirect/Get-patroon), gebruik dan 303 See Other.

Voor HTML-pagina’s met browsernavigatie zijn 301 en 302 meestal prima omdat GET nu eenmaal GET is. Voor API’s en formulieren hebben 308 en 307 de voorkeur om verrassende methode-herschrijvingen te voorkomen.

4xx: clientfouten (de juiste kiezen)

4xx betekent dat de client iets fout heeft gedaan. Hoe rijker je 4xx-vocabulaire, hoe makkelijker je API te gebruiken is. Clients kunnen op de code vertakken in plaats van foutstrings te parsen.

400 Bad Request

Generieke syntaxisfout. Ongeldige JSON, ontbrekend verplicht veld op structureel niveau, een verzoek dat de server niet eens kan parsen. Parseert het verzoek wel maar faalt het op business-validatie, kies dan 422.

401 Unauthorized vs 403 Forbidden

Het meest verwarde paar in HTTP. De splitsing is simpel zodra je het ziet:

  • 401 Unauthorized: het verzoek mist geldige authenticatie. De server weet niet wie je bent. De credentials opnieuw versturen (of de token verversen) kan het oplossen. Het antwoord moet een WWW-Authenticate-header bevatten volgens RFC 9110 §15.5.2.
  • 403 Forbidden: de server weet wie je bent en weigert toch. Het verzoek opnieuw versturen helpt niet. Je hebt andere credentials of andere permissies nodig.
Je zietWat klopt
401 met WWW-Authenticate: BearerGeen token, verlopen token of ongeldige token
403 na een succesvolle loginIngelogd, maar deze gebruiker mag deze resource niet benaderen
401 na een succesvolle loginBug; je wilt waarschijnlijk 403

Anti-patroon: 403-als-404. Sommige sites geven 403 terug als een niet-geauthenticeerde gebruiker /admin/dashboard opvraagt. Dat lekt het bestaan van /admin/dashboard. GitHub lost dit op door 404 terug te geven voor private repositories waar je geen lid van bent: de resource “bestaat niet” vanuit jouw perspectief. Dat is een bewuste keuze om informatie te verbergen, geen bug.

404 Not Found vs 410 Gone

Beide zeggen “deze resource is hier niet.” Het verschil zit in permanentie en SEO.

  • 404 Not Found: bestaat misschien, misschien niet, kan terugkomen. Zoekmachines blijven controleren.
  • 410 Gone: was hier, opzettelijk verwijderd, komt niet terug. Zoekmachines verwijderen het veel sneller uit de index.

Verwijder je een productpagina en wil je hem nu uit de Google-index, dan is 410 de juiste keuze. Is een URL slechts tijdelijk kapot, dan is 404 prima.

405 Method Not Allowed

De URL bestaat maar accepteert deze methode niet. Het antwoord moet een Allow-header bevatten met de ondersteunde methodes.

HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json

{"error": "POST is not allowed on this endpoint"}

De Allow-header vergeten is contractbreuk #1 in zelfgebouwde REST-API’s.

408 Request Timeout

De client begon een verzoek te sturen en werd toen stil. De server gaf het op. Dit verschilt van 504 Gateway Timeout, wat over de upstream gaat. 408 betekent “jij, de client, deed te lang.”

409 Conflict

Het verzoek conflicteert met de huidige staat. Meest voorkomend gebruik: optimistic locking. De client stuurt If-Match: "etag-v3" en de huidige ETag van de server is "etag-v4", dus de update wordt afgewezen met 409.

410 Gone

Zie hierboven: permanente verwijdering. Handig om soft-deleted records uit zoekindexen te halen.

415 Unsupported Media Type

De client heeft een body gestuurd die de server niet begrijpt. XML POSTen naar een JSON-only API levert 415 op. Het antwoord moet een hint geven over acceptabele types.

422 Unprocessable Content

Het verzoek parseert prima, maar faalt op semantische validatie. RFC 9110 heeft dit in 2022 eindelijk gepromoveerd van WebDAV naar de kernspecificatie. Gebruik 422 voor validatiefouten:

{
  "error": "validation_failed",
  "details": [
    {"field": "email", "message": "must be a valid email"},
    {"field": "age", "message": "must be at least 13"}
  ]
}

Kan je API niet kiezen tussen 400 en 422, dan is de vuistregel: 400 voor “ik kan dit niet eens parsen”, 422 voor “ik heb het geparsed en het slaat nergens op.”

425 Too Early

Verstuurd wanneer de server niet het risico wil lopen een verzoek te verwerken dat een TLS 1.3 early-data replay zou kunnen zijn. Vooral relevant voor CDN’s en reverse proxies.

428 Precondition Required

De server eist dat je If-Match of If-Unmodified-Since stuurt om het lost-update-probleem te vermijden. Gebruikt in API’s voor collaboratief bewerken.

429 Too Many Requests

Rate limited. Het antwoord moet Retry-After bevatten (in seconden, of als HTTP-datum) zodat goed gedragende clients kunnen terugschakelen.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{"error": "rate_limited", "limit": 100, "window": "1m"}

Het nummer is een verwijzing naar Bradbury. De use-case is niet fictief: DMCA-takedowns, AVG-recht-op-vergetelheid-verwijderingen en geo-blokkades op landniveau rechtvaardigen allemaal 451. Het antwoord moet een Link-header bevatten die naar de juridische instantie wijst die de blokkade oplegt, volgens RFC 7725.

418 I’m a Teapot (de easter egg)

Ja, hij is echt. RFC 2324 (1 april 1998) en de IETF hielden hem in de boeken omdat te veel producten hem als grap implementeerden. Lever 418 niet op in een echte API; de meeste reverse proxies en load balancers gaan er niet goed mee om.

Beslismatrix: welke 4xx?

SituatieCode
Body is ongeldig of niet te parsen400
Geen authenticatie / verlopen token401
Geauthenticeerd maar niet toegestaan403
URL bestaat niet (of je verbergt hem)404
URL bestond, opzettelijk verwijderd410
Verkeerde HTTP-methode405 (met Allow)
Verkeerde Content-Type415
Optimistic-lock conflict409
Validatiefout (parseert, valideert niet)422
Rate limited429 (met Retry-After)
Geblokkeerd om juridische redenen451

5xx: serverfouten (wat is er echt stuk)

5xx is “onze schuld.” On-call engineers geven het meest om welke 5xx ze om 3 uur ‘s nachts wakker maakte, omdat de code je vertelt welke laag je als eerste moet onderzoeken.

500 Internal Server Error

De vergaarbak. Bijna altijd een onafgehandelde exception die naar de standaard handler van het framework is opgeborreld. Hij vertelt je niets over de oorzaak; daarom is gestructureerde logging hier belangrijker dan de statuscode.

501 Not Implemented

De server ondersteunt de methode helemaal niet. Anders dan 405 (deze methode is niet toegestaan voor deze URL): 501 zegt “deze server heeft geen idee wat PROPFIND zelfs maar betekent.” Zelden in REST-API’s.

502 Bad Gateway

Een reverse proxy of load balancer ontving een ongeldig antwoord van de upstream. De upstream antwoordde wel, maar met rommel: verkeerd protocol, ongeldige headers, halverwege afgebroken verbinding. Zie je 502 van je CDN, dan crasht je origin waarschijnlijk of geeft truncated bodies terug.

503 Service Unavailable

De server bedient nu opzettelijk geen verzoeken. Gebruik dit voor onderhoudsvensters of nette antwoorden bij overbelasting. Hoort Retry-After te bevatten.

504 Gateway Timeout

De reverse proxy wachtte op de upstream en de upstream antwoordde niet op tijd. De upstream is traag of vastgelopen, anders dan 502, waar de upstream met rommel antwoordde.

502 vs 504: de on-call diagnose

Je zietEerste wat je controleert
502 Bad GatewayUpstream antwoordt met ongeldige data; controleer origin-logs op crashes, ongeldige antwoorden, protocol-mismatches
504 Gateway TimeoutUpstream hangt; controleer origin-CPU, DB-queries, downstream API-calls en de proxy_read_timeout van de proxy

Een veelvoorkomende verwarring: een database-query van 60 seconden komt naar boven als 504 als je proxy na 30 seconden time-out, maar als 500 als de app-server na 90 seconden time-out en een exception opwerpt. Zelfde root cause, andere code, andere logregel; zorg dat je dashboards beide tonen.

507 Insufficient Storage

WebDAV-specifiek. Schijf vol op de server. Zie je dit van een niet-WebDAV-API, dan rekt iemand de betekenis op.

508 Loop Detected

Oneindige recursie in WebDAV PROPFIND-operaties. Heel zeldzaam.

511 Network Authentication Required

Captive-portal-codes. De WiFi in een hotel of luchthaven stuurt 511 om je browser te vertellen “je moet eerst inloggen op het portaal.” Het antwoord bevat een Location naar de portaalpagina.

Troubleshooting-matrix: welke laag eerst controleren

CodeAppProxyDBNetwerk
500JaMisschien (onafgevangen DB-fout)
502Ja (upstream ongeldig)Misschien (TCP-reset)
503Ja (onderhoudsvlag)Ja (rate-limit reject)
504Ja (trage handler)Ja (timeout-config)Ja (trage query)Ja (DNS, packet loss)

Veelvoorkomende anti-patronen bij HTTP-statuscodes

Deze vijf fouten zijn samen goed voor het meeste slechte code dat ik review.

1. Fouten verpakken in 200 OK

HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}

Elke monitoring-tool, CDN en cache denkt nu dat het verzoek is geslaagd. Retry-logica faalt. Status-code-bewuste load balancers routeren slecht verkeer naar “gezonde” backends. Dit patroon kwam uit JSON-RPC en is overgenomen door GraphQL. GraphQL doet het omdat gedeeltelijke successen per-veld foutrapportage nodig hebben, wat begrijpelijk is. REST heeft geen excuus: gebruik 4xx voor clientfouten, 5xx voor serverfouten, en stop het gestructureerde detail in de body.

2. 401 en 403 door elkaar gebruiken

Zijn je 401 en 403 niet consistent, dan kunnen aanvallers je API aftasten om te ontdekken welke resources bestaan. Kies een beleid: of geef 404 terug voor “dit mag je niet zien” (de aanpak van GitHub voor private repos) of geef consequent 403 terug. Inconsistentie lekt informatie.

3. 403 verbergen achter 404

Soms juist, vaak een bug. GitHub die 404 teruggeeft voor private repos is bewust: het bestaan van de repo is zelf gevoelig. Maar als je API 404 teruggeeft voor “dit gebruikersaccount is geschorst”, kunnen legitieme gebruikers niet meer onderscheiden of ze de gebruikersnaam verkeerd hebben getypt of zijn geschorst. Documenteer je beleid expliciet en pas het consequent toe.

4. 500 als standaard catch gebruiken

Frameworks maken dit makkelijk, en daar zit het probleem. Elke onafgevangen exception wordt 500 en je alerting kan “database is down” niet onderscheiden van “gebruiker gaf een ongeldige UUID door.” Vang validatiefouten op en geef 400 of 422. Vang NotFound van je ORM op en geef 404. Reserveer 500 voor echt onverwachte storingen, en log een request-ID wanneer je hem opwerpt zodat je kunt correleren.

5. Lange redirect-ketens

Elke hop kost een round trip. Als /old naar /intermediate naar /canonical gaat, dan zijn dat twee extra DNS-lookups en twee extra TCP-handshakes (worst case). Google verlaagt specifiek de crawl-prioriteit voor ketens langer dan 3 hops, en browsers cappen redirect-ketens op rond de 20 om loops te voorkomen. Klap ketens in bij de bron: je CDN-config of de redirect-map van je applicatie.

HTTP-statuscodes en SEO

Zoekmachines behandelen statuscodes als gezaghebbende signalen over of ze een URL moeten houden, weghalen of overdragen. Doe ze fout en je rankings verschuiven.

301 Moved Permanently draagt PageRank over: Google behandelt de nieuwe URL als de canonieke bestemming van alle signalen die naar de oude URL wijzen. 302 Found draagt geen link equity over (of draagt het langzaam over, afhankelijk van Googles heuristieken). Heb je een URL voorgoed hernoemd, gebruik dan 301. Redirect je een gast naar /login, gebruik dan 302.

404 vs 410 vs Soft 404

Google onderscheidt drie “ontbrekende” toestanden:

  • 404 Not Found: Google controleert periodiek opnieuw en houdt de URL nog een tijdje in de index.
  • 410 Gone: Google verwijdert de URL sneller, vaak binnen één crawl-cyclus.
  • Soft 404: Googles term voor een pagina die 200 OK teruggeeft maar een “niet gevonden”-bericht toont. Google detecteert dit aan content-patronen en behandelt het toch als een 404, maar je hebt een crawl-verzoek verspild en mogelijk je echte content verwaterd.

Ben je een verouderde index aan het opschonen, geef dan echte 410’s terug voor permanent verwijderde URL’s.

5xx en crawl-budget

Googles crawler verlaagt zijn snelheid wanneer een site aanhoudend 5xx teruggeeft. Het Crawl Stats-rapport in Search Console laat dit zien. Een aanhoudende piek aan 5xx-fouten kan je crawl-budget dagenlang verlagen, wat betekent dat nieuwe pagina’s er langer over doen om geïndexeerd te worden. Behandel 5xx-percentages als een SEO-metric, niet alleen als een betrouwbaarheidsmetric.

200 OK die eigenlijk stuk is

200 OK teruggeven met een foutpagina (het soft-404-anti-patroon) is het slechtst denkbare scenario voor SEO. Google indexeert het foutbericht, rankt het voor niets, en realiseert zich langzaam dat de pagina stuk is. Geef altijd de juiste statuscode terug vanaf de server, ook al rendert je single-page app een vriendelijke fout-UI.

Hoe je HTTP-statuscodes inspecteert (tooling)

Je kunt niet repareren wat je niet kunt zien. Elke werkende ontwikkelaar zou vloeiend moeten zijn in minstens drie hiervan.

Browser DevTools Netwerk-paneel

Chrome, Firefox en Safari tonen allemaal een Status-kolom in het Netwerk-tabblad. Klik met de rechtermuisknop op de kolomkop om Status Text toe te voegen als die niet zichtbaar is. Handige trucs:

  • Preserve log: bewaar entries over navigaties heen zodat je de volledige redirect-keten ziet.
  • Filter by status: typ status-code:5xx (Chrome) om alleen serverfouten te zien.
  • Replay XHR: rechtermuisknop op een verzoek, dan Replay XHR om het opnieuw te draaien zonder de pagina te herladen.

Klap voor redirects het verzoek uit om elke hop en de statuscode bij elke stap te zien.

curl (het universele antwoord)

curl toont alles. Drie patronen die het meeste debuggen afdekken:

# Just the status code
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1

# Headers only (HEAD request, follow redirects)
curl -I -L https://example.com

# Full verbose with request and response headers
curl -v https://api.example.com/users/1

Wanneer je test-URL’s bouwt met speciale tekens in query strings, gebruik dan --data-urlencode zodat curl de encoding voor je afhandelt, of plak de URL in onze URL Decoder & Encoder om te verifiëren welke bytes er daadwerkelijk over de lijn gaan.

# curl encodes the query value for you
curl -G "https://api.example.com/search" \
  --data-urlencode "q=hello world & friends"

# Sends: GET /search?q=hello%20world%20%26%20friends

JavaScript fetch

De Response.status-property bevat de integer-code. Response.ok is true voor elke 2xx.

const res = await fetch('https://api.example.com/users/1');

console.log(res.status);      // 200
console.log(res.statusText);  // "OK"
console.log(res.ok);          // true

if (!res.ok) {
  if (res.status === 401) {
    // refresh token and retry
  } else if (res.status === 429) {
    const retryAfter = Number(res.headers.get('Retry-After')) || 1;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  } else if (res.status >= 500) {
    throw new Error(`Server error: ${res.status}`);
  }
}

In axios woont dezelfde logica in interceptors:

import axios from 'axios';

axios.interceptors.response.use(
  response => response,
  error => {
    const status = error.response?.status;
    if (status === 401) {
      // redirect to login
    }
    return Promise.reject(error);
  }
);

Python requests

import requests

r = requests.get('https://api.example.com/users/1')

print(r.status_code)  # 200
print(r.reason)       # 'OK'

# Raises requests.exceptions.HTTPError for 4xx/5xx
r.raise_for_status()

# Manual handling
if r.status_code == 429:
    retry_after = int(r.headers.get('Retry-After', '1'))
    time.sleep(retry_after)
elif 500 <= r.status_code < 600:
    raise RuntimeError(f'Server error: {r.status_code}')

raise_for_status() is het Python-idioom voor “maak luid bezwaar bij 4xx/5xx.” Gebruik het in scripts waar je liever exceptions wilt bij fouten dan vertakken op status_code.

Postman en Bruno

Beide laten je in een testscript asserten op statuscodes:

// Postman/Bruno test script
pm.test("Status is 201", () => {
  pm.response.to.have.status(201);
});

pm.test("Has Location header", () => {
  pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});

Draai deze in CI tegen staging om contractbreuken vóór productie te onderscheppen.

FAQ

Wat is het verschil tussen 401 en 403?

401 Unauthorized betekent dat de server niet weet wie je bent: je credentials ontbreken, zijn verlopen of zijn ongeldig. 403 Forbidden betekent dat de server weet wie je bent en toch weigert. Kunnen andere credentials het oplossen, gebruik dan 401. Zo niet, gebruik 403.

Wanneer gebruik ik 301 vs 302?

Gebruik 301 als de verhuizing permanent is: de oude URL komt nooit meer terug, en je wilt dat zoekmachines link equity overdragen naar de nieuwe URL. Gebruik 302 voor tijdelijke redirects waar de originele URL nog canoniek is (login-flows, A/B-testen, onderhoudspagina’s). Geef voor API’s de voorkeur aan 308 en 307 omdat ze de request-methode behouden.

Wat betekent een 502 Bad Gateway-fout?

502 betekent dat een reverse proxy of load balancer een ongeldig antwoord ontving van de upstream-server. De upstream antwoordde, maar met rommel: verkeerd protocol, ongeldige headers of een verbroken verbinding. Het verschilt van 504 Gateway Timeout, waarbij de upstream helemaal niet antwoordde. Eerste plek om te kijken: origin-server-logs op crashes of afgekapte antwoorden.

Wat is een “soft 404”?

Een “soft 404” is een pagina die 200 OK teruggeeft maar in werkelijkheid een “niet gevonden”-bericht toont. Google detecteert deze heuristisch en behandelt ze toch als 404’s. Ze verspillen crawl-budget en kunnen je echte content verwateren. Geef altijd echte 404- of 410-statuscodes terug vanaf de server, ook al rendert je single-page app een vriendelijke fout-UI.

Wanneer gebruik ik 422 in plaats van 400?

Gebruik 400 Bad Request als de server het verzoek niet eens kan parsen: ongeldige JSON, ontbrekende structurele velden, syntaxisfouten. Gebruik 422 Unprocessable Content als het verzoek prima parseert maar faalt op business-validatie: ongeldig e-mailformaat, waarde buiten bereik, semantisch inconsistente velden. De vuistregel: 400 voor syntaxis, 422 voor semantiek.

Hoe reageer ik op 429 Too Many Requests?

Lees de Retry-After-header (een aantal seconden of een HTTP-datum) en wacht minstens zo lang voordat je het opnieuw probeert. Ontbreekt Retry-After, gebruik dan exponential backoff met jitter, beginnend rond 1 seconde. Probeer nooit meteen opnieuw; zo word je geband.

Worden 1xx-informatieve codes in 2026 nog gebruikt?

Ja, maar de meeste zijn onzichtbaar voor applicatiecode. 100 Continue en 101 Switching Protocols zijn basale HTTP/1.1-features. 103 Early Hints wordt steeds vaker gebruikt door Cloudflare, Fastly en Vercel om preload-hints te pushen voordat het hoofdantwoord klaar is, en verbetert Largest Contentful Paint merkbaar. De meeste HTTP-libraries vouwen 1xx op in het uiteindelijke antwoord, dus je ziet ze meestal alleen in DevTools of curl -v.

Is 418 “I’m a teapot” een echte statuscode?

Ja, verrassend genoeg. RFC 2324 was een 1-april-grap uit 1998, maar genoeg producten implementeerden het, dus de IETF hield het in de boeken in RFC 7168. Lever 418 niet op in productie. Veel reverse proxies en load balancers gaan er niet correct mee om, en het dient geen echt doel buiten de grap.

Gerelateerde artikelen

Alle artikelen bekijken