uptimeMonitoruptimeMonitor
Back to Blog
Guides

The Developer's Guide to Writing Better Health Check Endpoints

A health check that just returns 200 OK is almost useless. Here's how to build health endpoints that give your monitoring real, actionable information.

UT
UptimeGuard Team
October 8, 20257 min read5,127 views
Share
health-checkapidevelopmentmonitoringbest-practices

The Developer's Guide to Writing Better Health Check Endpoints

Every monitoring system hits a health check endpoint. But what that endpoint returns determines whether your monitoring is actually useful or just giving you false confidence.

The Levels of Health Checks

Level 0: The Useless Health Check

GET /health → 200 OK

This tells you the web server process is running. That's it. The database could be down, the cache could be empty, and critical services could be unreachable.

Level 1: The Basic Health Check

GET /health → 200 OK
{ "status": "ok", "timestamp": "2026-03-27T10:00:00Z" }

Slightly better — the timestamp proves the response is fresh, not cached.

Level 2: The Dependency Health Check

GET /health → 200 OK
{
  "status": "ok",
  "dependencies": {
    "database": { "status": "ok", "latency_ms": 3 },
    "redis": { "status": "ok", "latency_ms": 1 },
    "stripe": { "status": "ok", "latency_ms": 145 }
  }
}

Now we know if dependencies are healthy and how fast they're responding.

Level 3: The Comprehensive Health Check

GET /health → 200 OK
{
  "status": "ok",
  "version": "2.4.1",
  "uptime_seconds": 86421,
  "dependencies": {
    "database": { "status": "ok", "latency_ms": 3, "connections": "45/100" },
    "redis": { "status": "ok", "latency_ms": 1, "memory": "234MB/512MB" },
    "queue": { "status": "ok", "depth": 12, "consumers": 4 }
  },
  "checks": {
    "disk_space": { "status": "ok", "used": "67%" },
    "memory": { "status": "ok", "used": "72%" }
  }
}

Best Practices

Return Appropriate Status Codes

  • 200 — Everything is healthy
  • 503 — Service is unhealthy (this tells load balancers to stop sending traffic)

Separate Liveness from Readiness

  • /health/live — Is the process running? (For orchestrator liveness probes)
  • /health/ready — Can it serve traffic? (For load balancer readiness)

Don't Make Health Checks Too Expensive

A health check that runs 5 database queries every 30 seconds can itself become a performance problem. Keep checks lightweight.

Include Version Information

Knowing which version is running helps correlate issues with deployments.

Protect Sensitive Details

Don't expose connection strings, credentials, or internal IPs. Health checks should provide status, not configuration.

Consider a Degraded State

Not everything is binary. If Redis is down but the app can function (slowly), report degraded rather than unhealthy:

{ "status": "degraded", "reason": "Redis unavailable, using database fallback" }

A good health check endpoint is the foundation of effective monitoring. Invest 30 minutes in building a proper one and every monitoring tool you use becomes more effective.

Share
UT

Written by

UptimeGuard Team

Related articles