Un endpoint de health check est une URL, généralement /health, qui indique à un moniteur ou à un load balancer si votre application peut servir des requêtes. Il exécute quelques vérifications rapides sur ce dont l’application dépend, comme la base de données, le cache et l’espace disque libre, et répond par HTTP 200 et {« status »: »ok »} quand tout fonctionne, ou par HTTP 503 et le nom de la vérification en échec quand ce n’est pas le cas. Gardez-le rapide, peu coûteux, hors cache et sans secrets, puis pointez dessus un moniteur de disponibilité qui valide la réponse, pas seulement le code de statut.
Ce guide couvre la différence entre liveness et readiness, ce qu’il faut vérifier, le format de réponse, des exemples fonctionnels en PHP avec Laravel, Node avec Express et Python avec Flask, et la façon de surveiller l’endpoint une fois en place.
Pourquoi la page d’accueil n’est pas un health check
La plupart des équipes commencent par surveiller la page d’accueil. C’est une première étape raisonnable, mais la page d’accueil est souvent servie depuis un CDN ou un cache de pages : elle continue de répondre 200 alors que la base de données est en panne et que plus personne ne peut se connecter ni payer. Un endpoint dédié contourne le cache et sollicite directement les dépendances. Il vous donne aussi une URL unique et prévisible, avec une réponse courte et stable, facile à valider.
Liveness ou readiness
Les plateformes de conteneurs comme Kubernetes divisent la santé en deux questions, et cette distinction est utile partout :
Liveness : le processus est-il vivant et non bloqué ? Sinon, redémarrez-le. Un contrôle de liveness ne doit pas tester la base de données ni d’autres services. Si la base tombe et que toutes les instances de l’application échouent à leur contrôle de liveness, la plateforme les redémarre toutes, ce qui ne répare pas la base et ralentit la reprise.
Readiness : cette instance peut-elle servir de vraies requêtes en ce moment ? Sinon, cessez de lui envoyer du trafic. C’est ici que les vérifications des dépendances ont leur place.
Une organisation courante : /livez ou /up pour la liveness, qui renvoie 200 tant que l’application démarre, et /health ou /readyz pour la readiness. Pour la surveillance de disponibilité externe, c’est l’endpoint de readiness qui compte, car il répond à la question qui intéresse vos clients : l’application fonctionne-t-elle ?
Ce qu’il faut vérifier
Vérifiez les dépendances sans lesquelles une requête normale échouerait, et rien de plus :
Base de données : exécutez SELECT 1 sur le même pool de connexions que celui de l’application. Cela prouve que le serveur est joignable et accepte les requêtes.
Cache ou stockage des sessions : écrivez puis lisez une clé à courte durée de vie dans Redis ou Memcached. Si les sessions y sont stockées, la connexion en dépend.
Disque : vérifiez l’espace libre sur le volume où l’application écrit, pour les fichiers envoyés, les logs ou les sessions. Échouez quand il passe sous une marge, par exemple 10 %.
File d’attente : vérifiez que le backend de la file répond et, éventuellement, que la plus ancienne tâche en attente ne dépasse pas une limite que vous choisissez. Un arriéré qui grossit signifie que les workers se sont arrêtés.
Méfiez-vous des API tierces. Si votre endpoint appelle un prestataire de paiement à chaque contrôle et que ce prestataire est lent, votre health check échoue alors que votre propre application va bien, et un load balancer peut retirer toutes les instances de la rotation. Surveillez plutôt les services tiers avec leurs propres moniteurs, pour qu’une alerte vous dise de qui vient le problème. La surveillance des services tiers d’Uptimecheck suit les pages de statut de 31 services comme Klarna, Swish, Shopify, Cloudflare et OpenAI, ou de n’importe quelle page hébergée sur Statuspage.
Le garder rapide et peu coûteux
Un health check tourne chaque minute depuis chaque moniteur et chaque load balancer, jour et nuit. Il ne doit jamais devenir ce qui surcharge l’application.
Donnez à chaque vérification de dépendance son propre délai d’attente court, par exemple une ou deux secondes, pour qu’un service bloqué ne bloque pas l’endpoint.
Exécutez les vérifications en parallèle quand votre langage le permet facilement.
Utilisez des requêtes triviales. Ne comptez jamais les lignes d’une grande table et ne lancez jamais de rapport.
Si de nombreux outils de contrôle sollicitent l’endpoint, mettez le résultat en cache en mémoire pendant quelques secondes.
Envoyez Cache-Control: no-store et excluez le chemin des caches de pages et des CDN, pour ne jamais voir un « ok » en cache.
Évitez les sessions, les cookies et les middlewares d’authentification dont l’endpoint n’a pas besoin.
Ne divulguez pas de secrets
Un endpoint de santé public est visible par quiconque devine /health. Renvoyez le nom et l’état de chaque vérification, et rien d’autre. N’incluez ni messages d’exception, ni chaînes de connexion, ni noms d’hôtes, ni adresses IP internes, ni versions de logiciels, ni variables d’environnement. Consignez plutôt l’erreur détaillée dans les logs du serveur. Si vous voulez une vue de diagnostic plus riche, placez-la sur un chemin séparé derrière une authentification ou réservez-la à votre réseau interne.
Format de réponse et codes de statut
Gardez un JSON court et stable :
{
« status »: « ok »,
« checks »: {
« database »: « ok »,
« cache »: « ok »,
« disk »: « ok »,
« queue »: « ok »
}
}
Quand une vérification échoue, mettez sa valeur à « fail », passez le statut global à « fail » et changez le code de statut HTTP :
200 OK quand toutes les vérifications obligatoires passent.
503 Service Unavailable quand une vérification obligatoire échoue. C’est le code qu’attendent les load balancers et les moniteurs pour « temporairement incapable de servir ».
Éventuellement, 200 avec « status »: »degraded » pour une dépendance facultative en panne alors que le cœur fonctionne encore. Décidez dès le départ quelles vérifications sont obligatoires.
Évitez de renvoyer 200 avec « status »: »fail » dans le corps. Beaucoup d’outils ne lisent que le code de statut. Consultez les codes de statut HTTP pour la surveillance pour savoir comment chaque code est généralement traité.
Exemple : PHP et Laravel
Les versions récentes de Laravel fournissent une route /up simple qui confirme que l’application démarre, ce qui fait un bon contrôle de liveness. Pour la readiness, ajoutez une route qui vérifie les dépendances :
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’);
});
Enregistrez-la en dehors des groupes de middlewares qui démarrent des sessions, et définissez un délai de connexion court pour la base de données dans votre configuration.
Exemple : Node et 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 });
});
Les vérifications s’exécutent en parallèle, chacune limitée à deux secondes : l’endpoint répond donc en deux secondes environ, même quand une dépendance est bloquée.
Exemple : Python et 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 »}
Ajoutez une vérification Redis de la même façon si vous l’utilisez pour les sessions ou une file de tâches.
Testez le cas d’échec
Un health check qui n’a jamais échoué n’a jamais été testé. En préproduction, arrêtez la base de données ou pointez l’application vers un mauvais hôte de cache, et confirmez que l’endpoint renvoie 503 dans votre délai et nomme la bonne vérification. Confirmez ensuite que la réponse ne contient aucun texte d’erreur. Cela prend dix minutes et vous garantit que l’endpoint dira la vérité quand ce sera important.
Surveiller l’endpoint
Un endpoint que personne ne contrôle depuis l’extérieur, c’est la moitié du travail. Pointez dessus un moniteur HTTP situé hors de votre réseau, pour qu’il détecte aussi les problèmes de DNS, de TLS, de pare-feu et de load balancer :
Surveillez https://lumenanalytics.io/health en GET toutes les minutes.
Attendez le statut 200 et vérifiez que le corps contient « status »: »ok ». Une page d’erreur de proxy qui renverrait 200 fait alors quand même échouer le contrôle.
Envoyez un en-tête user agent personnalisé, pour filtrer facilement le trafic de surveillance dans vos statistiques et vos logs.
Suivez l’historique des temps de réponse. Un health check qui passe peu à peu de 40 ms à 900 ms annonce une base de données en difficulté.
Envoyez l’alerte aux personnes responsables du service.
La surveillance d’API d’Uptimecheck prend en charge les méthodes et en-têtes personnalisés (y compris l’authentification basic ou un bearer token) et valide le statut attendu, les mots-clés et les champs JSON : cette configuration prend donc une minute. Gardez à côté un moniteur de disponibilité classique sur la page d’accueil et sur la page de conversion la plus importante ; la checklist de surveillance d’API couvre les autres endpoints à surveiller.
Les workers de file d’attente et les tâches planifiées tournent souvent en dehors du processus web : un endpoint de santé ne peut donc pas voir s’ils se sont arrêtés. Donnez-leur plutôt des heartbeats : le worker appelle une URL toutes les quelques minutes, et un ping manquant déclenche l’alerte.
Checklist de l’endpoint de health check
Séparez la liveness (processus vivant) de la readiness (dépendances fonctionnelles).
Vérifiez la base de données, le cache, le disque et la file d’attente dont l’application a besoin pour servir une requête.
Donnez à chaque vérification un délai court et gardez des requêtes triviales.
Renvoyez 200 quand tout va bien et 503 quand une vérification obligatoire échoue.
Ne renvoyez que les noms et états des vérifications. Pas d’erreurs, de versions ni de noms d’hôtes.
Désactivez la mise en cache pour ce chemin.
Surveillez-le toutes les minutes depuis l’extérieur, avec validation de la réponse.
Uptimecheck surveille les endpoints de santé, les API, les serveurs et les tâches planifiées dans un seul compte. Démarrez un essai gratuit de 14 jours, sans carte bancaire.