Un endpoint de health check es una URL, normalmente /health, que indica a un monitor o a un balanceador de carga si tu aplicación puede atender peticiones. Ejecuta unas cuantas comprobaciones rápidas sobre aquello de lo que depende la aplicación, como la base de datos, la caché y el espacio libre en disco, y responde con HTTP 200 y {«status»:»ok»} cuando todo funciona, o con HTTP 503 y el nombre de la comprobación que falla cuando algo no va bien. Hazlo rápido, barato, sin caché y sin secretos, y apunta a él un monitor de uptime que valide la respuesta, no solo el código de estado.
Esta guía explica la diferencia entre liveness y readiness, qué comprobar, el formato de la respuesta, ejemplos funcionales para PHP con Laravel, Node con Express y Python con Flask, y cómo monitorizar el endpoint una vez creado.
Por qué la página de inicio no es un health check
La mayoría de los equipos empiezan monitorizando la página de inicio. Es un primer paso razonable, pero la página de inicio suele servirse desde un CDN o una caché de páginas, así que sigue respondiendo 200 mientras la base de datos está caída y nadie puede iniciar sesión ni completar una compra. Un endpoint dedicado se salta la caché y pone a prueba directamente las dependencias. Además, te da una única URL predecible con una respuesta pequeña y estable que es fácil de validar.
Liveness frente a readiness
Las plataformas de contenedores como Kubernetes dividen la salud en dos preguntas, y esa división es útil en cualquier entorno:
Liveness: ¿el proceso está vivo y no se ha quedado bloqueado? Si no, reinícialo. Una comprobación de liveness no debería probar la base de datos ni otros servicios. Si la base de datos se cae y todas las instancias de la aplicación fallan su comprobación de liveness, la plataforma las reinicia todas, lo que no arregla la base de datos y hace que la recuperación sea más lenta.
Readiness: ¿esta instancia puede atender peticiones reales ahora mismo? Si no, deja de enviarle tráfico. Aquí es donde van las comprobaciones de dependencias.
Una estructura habitual es /livez o /up para liveness, que devuelve 200 siempre que la aplicación arranque, y /health o /readyz para readiness. Para la monitorización de uptime externa, el que interesa es el endpoint de readiness, porque responde a la pregunta que les importa a tus clientes: ¿funciona la aplicación?
Qué comprobar
Comprueba las dependencias sin las que una petición normal fallaría, y nada más:
Base de datos: ejecuta SELECT 1 en el mismo pool de conexiones que usa la aplicación. Demuestra que el servidor es accesible y acepta consultas.
Caché o almacén de sesiones: escribe y lee una clave de vida corta en Redis o Memcached. Si las sesiones se guardan ahí, el inicio de sesión depende de ello.
Disco: comprueba el espacio libre del volumen en el que escribe la aplicación, ya sea para subidas, logs o sesiones. Haz que falle cuando baje de un margen, por ejemplo el 10 por ciento.
Cola: comprueba que el backend de la cola responde y, si quieres, que el trabajo más antiguo en espera no supera un límite que tú elijas. Una cola que no deja de crecer significa que los workers se han parado.
Ten cuidado con las APIs de terceros. Si tu endpoint llama a un proveedor de pagos en cada comprobación y el proveedor va lento, tu health check falla aunque tu propia aplicación esté bien, y un balanceador de carga puede sacar de rotación todas las instancias. Es mejor monitorizar a los terceros con sus propios monitores, para que una alerta te diga de quién es el problema. La monitorización de terceros de Uptimecheck sigue las páginas de estado de 31 servicios como Klarna, Swish, Shopify, Cloudflare y OpenAI, o cualquier página alojada en Statuspage.
Hazlo rápido y barato
Un health check se ejecuta cada minuto desde cada monitor y cada balanceador de carga, las 24 horas del día. Nunca debe convertirse en lo que sobrecarga la aplicación.
Da a cada comprobación de dependencias su propio timeout corto, por ejemplo de uno o dos segundos, para que un servicio que se cuelga no cuelgue el endpoint.
Ejecuta las comprobaciones en paralelo cuando tu lenguaje lo ponga fácil.
Usa consultas triviales. Nunca cuentes las filas de una tabla grande ni generes un informe.
Si muchos comprobadores consultan el endpoint, guarda el resultado en memoria durante unos segundos.
Envía Cache-Control: no-store y excluye la ruta de las cachés de páginas y de los CDN, para no ver nunca un «ok» en caché.
Prescinde de las sesiones, las cookies y el middleware de autenticación que el endpoint no necesite.
No filtres secretos
Un endpoint de salud público está a la vista de cualquiera que adivine /health. Devuelve el nombre y el estado de cada comprobación, y nada más. No incluyas mensajes de excepción, cadenas de conexión, nombres de host, direcciones IP internas, versiones de software ni variables de entorno. Registra el error detallado en el servidor. Si quieres una vista de diagnóstico más completa, ponla en una ruta aparte protegida con autenticación o limítala a tu red interna.
Formato de la respuesta y códigos de estado
Mantén el JSON pequeño y estable:
{
«status»: «ok»,
«checks»: {
«database»: «ok»,
«cache»: «ok»,
«disk»: «ok»,
«queue»: «ok»
}
}
Cuando una comprobación falla, pon su valor a «fail», pon el estado general a «fail» y cambia el código de estado HTTP:
200 OK cuando pasan todas las comprobaciones obligatorias.
503 Service Unavailable cuando falla una comprobación obligatoria. Es el código que los balanceadores de carga y los monitores esperan para «temporalmente no puedo atender».
Opcionalmente, 200 con «status»:»degraded» para una dependencia opcional que está caída mientras el núcleo sigue funcionando. Decide de antemano qué comprobaciones son obligatorias.
Evita devolver 200 con «status»:»fail» en el cuerpo. Muchas herramientas solo leen el código de estado. Consulta códigos de estado HTTP para la monitorización para ver cómo se suele tratar cada código.
Ejemplo: PHP y Laravel
Las versiones recientes de Laravel incluyen una ruta sencilla /up que confirma que la aplicación arranca, lo que sirve como buena comprobación de liveness. Para readiness, añade una ruta que compruebe las dependencias:
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’);
});
Regístrala fuera de los grupos de middleware que inician sesiones y configura un timeout de conexión corto para la base de datos.
Ejemplo: Node y 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 });
});
Las comprobaciones se ejecutan en paralelo, cada una con un límite de dos segundos, así que el endpoint responde en unos dos segundos aunque una dependencia se cuelgue.
Ejemplo: Python y 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»}
Añade una comprobación de Redis del mismo modo si lo usas para las sesiones o para una cola de tareas.
Prueba el camino del fallo
Un health check que nunca ha fallado nunca se ha probado. En staging, detén la base de datos o apunta la aplicación a un host de caché incorrecto y confirma que el endpoint devuelve 503 dentro de tu timeout y señala la comprobación correcta. Después confirma que la respuesta no contiene ningún texto de error. Lleva diez minutos y te garantiza que el endpoint dirá la verdad cuando importe.
Monitorizar el endpoint
Un endpoint que nadie comprueba desde fuera es solo la mitad del trabajo. Apunta a él un monitor HTTP desde fuera de tu red, para detectar también problemas de DNS, TLS, firewall y balanceador de carga:
Monitoriza https://lumenanalytics.io/health con GET cada minuto.
Espera el estado 200 y valida que el cuerpo contiene «status»:»ok». Así, una página de error de un proxy que casualmente devuelva 200 seguirá haciendo fallar la comprobación.
Envía una cabecera de user agent personalizada, para que el tráfico de monitorización sea fácil de filtrar en la analítica y los logs.
Vigila el historial de tiempos de respuesta. Un health check que pasa poco a poco de 40 ms a 900 ms es un aviso temprano de una base de datos con problemas.
Envía la alerta a las personas responsables del servicio.
La monitorización de APIs de Uptimecheck admite métodos y cabeceras personalizados (incluida la autenticación básica o un bearer token) y valida el estado esperado, las palabras clave y los campos JSON, así que esta configuración lleva un minuto. Mantén a la vez un monitor de uptime normal en la página de inicio y en la página de conversión más importante; la checklist de monitorización de APIs repasa los demás endpoints que vale la pena vigilar.
Los workers de colas y las tareas programadas suelen ejecutarse fuera del proceso web, así que un endpoint de salud no puede ver si se han parado. Para ellos, usa heartbeats: el worker hace ping a una URL cada pocos minutos, y si falta un ping salta la alerta.
Checklist del endpoint de health check
Separa liveness (el proceso está vivo) de readiness (las dependencias funcionan).
Comprueba la base de datos, la caché, el disco y la cola que la aplicación necesita para atender una petición.
Da a cada comprobación un timeout corto y mantén las consultas triviales.
Devuelve 200 cuando todo está bien y 503 cuando falla una comprobación obligatoria.
Devuelve solo los nombres y estados de las comprobaciones. Ni errores, ni versiones, ni nombres de host.
Desactiva la caché para la ruta.
Monitorízalo cada minuto desde fuera, con validación de la respuesta.
Uptimecheck monitoriza endpoints de salud, APIs, servidores y tareas programadas desde una sola cuenta. Empieza una prueba gratuita de 14 días, sin tarjeta de crédito.