Een healthcheck-endpoint is een URL, meestal /health, die een monitor of load balancer vertelt of je applicatie verzoeken kan verwerken. Het voert een paar snelle checks uit op de onderdelen waarvan de app afhankelijk is, zoals de database, de cache en de vrije schijfruimte, en antwoordt met HTTP 200 en {“status”:”ok”} als alles werkt, of met HTTP 503 en de naam van de falende check als er iets misgaat. Houd het snel, licht, ongecached en vrij van geheimen, en laat er een uptime monitor op wijzen die de response valideert, niet alleen de statuscode.
Deze gids behandelt het verschil tussen liveness en readiness, wat je controleert, het responseformaat, werkende voorbeelden voor PHP met Laravel, Node met Express en Python met Flask, en hoe je het endpoint monitort zodra het er is.
Waarom de homepage geen healthcheck is
De meeste teams beginnen met het monitoren van de homepage. Een redelijke eerste stap, maar de homepage komt vaak uit een CDN of paginacache en blijft dus 200 antwoorden terwijl de database plat ligt en niemand kan inloggen of afrekenen. Een apart endpoint slaat de cache over en test de afhankelijkheden rechtstreeks. Je krijgt bovendien één voorspelbare URL met een kleine, stabiele response die makkelijk te valideren is.
Liveness versus readiness
Containerplatforms zoals Kubernetes splitsen health op in twee vragen, en die splitsing is overal nuttig:
Liveness: leeft het proces en hangt het niet vast? Zo niet, herstart het. Een liveness-check hoort de database of andere diensten niet te testen. Valt de database uit en faalt elke app-instantie op haar liveness-check, dan herstart het platform ze allemaal. Dat lost het databaseprobleem niet op en vertraagt het herstel.
Readiness: kan deze instantie op dit moment echte verzoeken verwerken? Zo niet, stuur er dan geen verkeer meer heen. Hier horen de checks op afhankelijkheden thuis.
Een gangbare indeling is /livez of /up voor liveness, dat 200 teruggeeft zolang de app opstart, en /health of /readyz voor readiness. Voor externe uptime monitoring is het readiness-endpoint het interessante, omdat het de vraag beantwoordt waar je klanten om geven: werkt de app?
Wat je controleert
Controleer de afhankelijkheden zonder welke een normaal verzoek zou mislukken, en verder niets:
Database: voer SELECT 1 uit op dezelfde connection pool die de app gebruikt. Dat bewijst dat de server bereikbaar is en query’s accepteert.
Cache of sessieopslag: schrijf en lees een kortlevende sleutel in Redis of Memcached. Staan sessies daar, dan hangt inloggen ervan af.
Schijf: controleer de vrije ruimte op het volume waar de app naar schrijft, voor uploads, logs of sessies. Laat de check falen als die onder een marge zakt, bijvoorbeeld 10 procent.
Queue: controleer of de queue-backend antwoordt, en eventueel of de oudste wachtende job niet ouder is dan een grens die je zelf kiest. Een groeiende achterstand betekent dat de workers zijn gestopt.
Wees voorzichtig met API’s van derden. Roept je endpoint bij elke check een betaalprovider aan en is die provider traag, dan faalt je healthcheck terwijl je eigen app prima werkt, en kan een load balancer alle instanties uit de rotatie halen. Bewaak derden liever met eigen monitors, zodat een alert je vertelt wiens probleem het is. Monitoring van derden in Uptimecheck volgt de statuspagina’s van 31 diensten, zoals Klarna, Swish, Shopify, Cloudflare en OpenAI, of elke pagina die op Statuspage draait.
Houd het snel en licht
Een healthcheck draait elke minuut vanaf elke monitor en load balancer, dag en nacht. Hij mag nooit zelf de oorzaak worden dat de app overbelast raakt.
Geef elke check op een afhankelijkheid een eigen korte timeout, bijvoorbeeld een of twee seconden, zodat één hangende dienst niet het hele endpoint laat hangen.
Voer checks parallel uit waar je programmeertaal dat makkelijk maakt.
Gebruik triviale query’s. Tel nooit rijen in een grote tabel en draai geen rapport.
Raken veel checkers het endpoint, cache het resultaat dan een paar seconden in het geheugen.
Stuur Cache-Control: no-store mee en sluit het pad uit van paginacaches en CDN’s, zodat je nooit een gecachte “ok” ziet.
Sla sessies, cookies en authenticatie-middleware over die het endpoint niet nodig heeft.
Lek geen geheimen
Een openbaar health-endpoint is zichtbaar voor iedereen die /health raadt. Geef de naam en status van elke check terug en verder niets. Neem geen foutmeldingen, connection strings, hostnamen, interne IP-adressen, softwareversies of omgevingsvariabelen op. Log de gedetailleerde fout in plaats daarvan op de server. Wil je een uitgebreidere diagnostische weergave, zet die dan op een apart pad achter authenticatie of beperk hem tot je interne netwerk.
Responsevorm en statuscodes
Houd de JSON klein en stabiel:
{
“status”: “ok”,
“checks”: {
“database”: “ok”,
“cache”: “ok”,
“disk”: “ok”,
“queue”: “ok”
}
}
Faalt een check, zet dan de waarde ervan op “fail”, zet de status op het hoogste niveau op “fail” en verander de HTTP-statuscode:
200 OK als elke verplichte check slaagt.
503 Service Unavailable als een verplichte check faalt. Dat is de code die load balancers en monitors verwachten voor “kan tijdelijk geen verzoeken verwerken”.
Eventueel 200 met “status”:”degraded” voor een optionele afhankelijkheid die down is terwijl de kern nog werkt. Bepaal vooraf welke checks verplicht zijn.
Geef geen 200 terug met “status”:”fail” in de body. Veel tools lezen alleen de statuscode. Zie HTTP-statuscodes voor monitoring voor hoe elke code meestal wordt behandeld.
Voorbeeld: PHP en Laravel
Recente Laravel-versies bevatten een eenvoudige route /up die bevestigt dat de app opstart, een goede liveness-check. Voeg voor readiness een route toe die de afhankelijkheden controleert:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;
Route::get(‘/health’, function () {
$checks = [];
try {
DB::select(‘SELECT 1’);
$checks[‘database’] = ‘ok’;
} catch (\Throwable $e) {
report($e);
$checks[‘database’] = ‘fail’;
}
try {
Cache::put(‘health-check’, ‘ok’, 10);
$checks[‘cache’] = Cache::get(‘health-check’) === ‘ok’ ? ‘ok’ : ‘fail’;
} catch (\Throwable $e) {
report($e);
$checks[‘cache’] = ‘fail’;
}
$path = storage_path();
$checks[‘disk’] = disk_free_space($path) / disk_total_space($path) > 0.10 ? ‘ok’ : ‘fail’;
$healthy = ! in_array(‘fail’, $checks, true);
return response()
->json([‘status’ => $healthy ? ‘ok’ : ‘fail’, ‘checks’ => $checks], $healthy ? 200 : 503)
->header(‘Cache-Control’, ‘no-store’);
});
Registreer de route buiten middlewaregroepen die sessies starten, en stel in je config een korte verbindingstimeout voor de database in.
Voorbeeld: Node en Express
const withTimeout = (promise, ms) =>
Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error(’timeout’)), ms)),
]);
const check = (fn) => withTimeout(fn(), 2000).then(() => ‘ok’, () => ‘fail’);
app.get(‘/health’, async (req, res) => {
const [database, cache] = await Promise.all([
check(() => pool.query(‘SELECT 1’)),
check(() => redis.ping()),
]);
const checks = { database, cache };
const healthy = Object.values(checks).every((v) => v === ‘ok’);
res
.set(‘Cache-Control’, ‘no-store’)
.status(healthy ? 200 : 503)
.json({ status: healthy ? ‘ok’ : ‘fail’, checks });
});
De checks draaien parallel, elk met een limiet van twee seconden, zodat het endpoint binnen ongeveer twee seconden antwoordt, ook als een afhankelijkheid hangt.
Voorbeeld: Python en Flask
import shutil
from flask import jsonify
from sqlalchemy import text
@app.get(“/health”)
def health():
checks = {}
try:
db.session.execute(text(“SELECT 1”))
checks[“database”] = “ok”
except Exception:
app.logger.exception(“health: database”)
checks[“database”] = “fail”
usage = shutil.disk_usage(“/”)
checks[“disk”] = “ok” if usage.free / usage.total > 0.10 else “fail”
healthy = all(value == “ok” for value in checks.values())
body = {“status”: “ok” if healthy else “fail”, “checks”: checks}
return jsonify(body), 200 if healthy else 503, {“Cache-Control”: “no-store”}
Voeg op dezelfde manier een Redis-check toe als je Redis gebruikt voor sessies of een taakqueue.
Test het faalpad
Een healthcheck die nooit heeft gefaald, is nooit getest. Stop in staging de database of laat de app naar een verkeerde cachehost wijzen, en controleer dat het endpoint binnen je timeout 503 teruggeeft en de juiste check noemt. Controleer daarna dat de response geen fouttekst bevat. Het kost tien minuten en je weet dan dat het endpoint eerlijk is wanneer het erop aankomt.
Het endpoint monitoren
Een endpoint dat niemand van buitenaf controleert, is maar half werk. Laat er een HTTP-monitor van buiten je netwerk op wijzen, zodat die ook problemen met DNS, TLS, de firewall en de load balancer opvangt:
Monitor https://lumenanalytics.io/health elke minuut met GET.
Verwacht status 200 en valideer dat de body “status”:”ok” bevat. Een foutpagina van een proxy die toevallig 200 teruggeeft, laat de check dan toch falen.
Stuur een eigen user agent-header mee, zodat monitoringverkeer makkelijk uit analytics en logs te filteren is.
Houd de geschiedenis van de responstijd in de gaten. Een healthcheck die langzaam van 40 ms naar 900 ms kruipt, is een vroeg waarschuwingssignaal van een database die het zwaar heeft.
Stuur de alert naar de mensen die eigenaar zijn van de dienst.
API-monitoring van Uptimecheck ondersteunt eigen methoden en headers (inclusief basic auth of een bearer token) en valideert de verwachte status, trefwoorden en JSON-velden, dus deze opzet kost je een minuut. Houd daarnaast een gewone uptime monitor op de homepage en op de belangrijkste conversiepagina; de checklist voor API-monitoring behandelt de andere endpoints die het bewaken waard zijn.
Queue-workers en geplande taken draaien vaak buiten het webproces, dus een health-endpoint kan niet zien of ze zijn gestopt. Geef ze in plaats daarvan heartbeats: de worker pingt om de paar minuten een URL, en een ontbrekende ping zorgt voor een alert.
Checklist voor een healthcheck-endpoint
Scheid liveness (proces leeft) van readiness (afhankelijkheden werken).
Controleer de database, cache, schijf en queue die de app nodig heeft om een verzoek te verwerken.
Geef elke check een korte timeout en houd query’s triviaal.
Geef 200 terug als alles gezond is en 503 als een verplichte check faalt.
Geef alleen namen en status van checks terug. Geen fouten, versies of hostnamen.
Schakel caching uit voor het pad.
Monitor het elke minuut van buitenaf, met validatie van de response.
Uptimecheck bewaakt health-endpoints, API’s, servers en geplande taken in één account. Start een gratis proefperiode van 14 dagen, geen creditcard nodig.