Quick answer: To add a health check to Next.js (App Router), create app/api/health/route.ts that returns JSON with a 200 status when healthy and 503 when a dependency is down:

// app/api/health/route.ts
import { NextResponse } from 'next/server'

export const dynamic = 'force-dynamic' // never cache a health check

export async function GET() {
  try {
    // check your critical dependency (e.g. the database)
    await db.query('SELECT 1')
    return NextResponse.json(
      { status: 'healthy', timestamp: new Date().toISOString() },
      { status: 200 }
    )
  } catch (err) {
    return NextResponse.json(
      { status: 'unhealthy', error: String(err) },
      { status: 503 }
    )
  }
}

Then point an external monitor at https://yourapp.com/api/health so you get alerted when it returns 503. The full guide below covers database, Redis, and external-service checks, the Pages Router version, and multi-region monitoring.

A health check endpoint is the simplest and most effective way to know if your Next.js app is running correctly. It's a dedicated route that returns the status of your application and its dependencies (database, cache, external APIs) in a machine-readable format.

In this guide, you'll create a health check endpoint in Next.js and then set up automated external monitoring to check it from multiple geographic regions.

What Is a Health Check Endpoint?

A health check is a lightweight API route (typically GET /api/health) that verifies your application can serve requests and that critical dependencies are reachable. It should return a JSON response with the overall status and individual component statuses.

A good health check verifies three things: (1) the application process is running, (2) the database connection is active, and (3) critical external services are reachable.

Step 1: Create a Basic Health Endpoint

In Next.js App Router, create app/api/health/route.ts that returns a JSON object with status, timestamp, and process uptime. Return HTTP 200 for healthy, 503 for unhealthy.

The simplest version just confirms the app is running. Return { "status": "healthy", "timestamp": "...", "uptime": 12345 } with a 200 status code.

Step 2: Add Dependency Checks

A basic health check that always returns 200 isn't very useful. Add checks for your critical dependencies:

  • DatabaseRun a simple query like SELECT 1 and measure the latency. If it fails or takes too long, mark the database as unhealthy.
  • External APIsIf your app depends on Stripe, Auth0, or any third-party API, ping their health endpoints and verify they respond.
  • Memory usageCheck process.memoryUsage() and flag if heap usage exceeds 90% of available memory.

Return a structured response that lists each dependency with its status and latency. If any dependency is unhealthy, return HTTP 503 instead of 200. This lets external monitoring tools detect partial failures.

Step 3: Monitor It Externally

A health check endpoint that nobody checks is useless. You need an external service that calls your /api/health route regularly from outside your infrastructure.

Why external? Because if your server crashes, it can't check itself. You need checks from a completely separate system.

Point an uptime monitoring service at your /api/health URL with a 1 to 5 minute interval, ideally checking from more than one region. If you want a minimal do-it-yourself version first, a scheduled job running outside your hosting provider (a cron on another server or a scheduled CI workflow) can do it:

    // check-health.mjs, run every few minutes from outside your infrastructure
const res = await fetch('https://your-app.com/api/health', {
  signal: AbortSignal.timeout(5000),
})
if (!res.ok) {
  // send the alert to your team channel here
  console.error(`Health check failed: ${res.status}`)
  process.exit(1)
}

Good monitoring tools also record more than the HTTP status code: DNS lookup time, TLS handshake time, Time to First Byte (TTFB) and response size. That way you see not just IF your health check passed, but HOW FAST it responded from each location.

Step 4: Configure Alerts

Whatever tool runs the checks, set up these alert rules for your health endpoint:

  • API Downtriggers when the endpoint returns a non-200 status or is unreachable
  • High Latencytriggers when response time exceeds your threshold (e.g., 2000ms)
  • Error Ratetriggers when the failure percentage exceeds your threshold
  • SSL Expiryalerts before your certificate expires

Send alerts to the channel your team actually watches (Slack, email, SMS or a pager). Aim for one notification when an incident starts and one when it resolves, not a flood of repeated alerts on every failed check.

Multi-Region Health Checks

Your app might be healthy from Virginia but failing from Tokyo. Multi-region checks detect geographic issues that single-location monitoring misses. Common causes include CDN misconfigurations, DNS propagation delays, and regional infrastructure outages.

Check from a few regions that match where your users are (for example the US, South America, Europe and Asia). If your health check fails from one region but passes from others, you know it's a regional issue, not a full outage.

Best Practices

  • Keep it fastHealth checks should respond in under 500ms. Don't query every table; a simple SELECT 1 is enough for database checks.
  • Don't cacheHealth checks should always run fresh. Add Cache-Control: no-cache headers.
  • Include version infoReturn your app version or git commit hash. Useful for verifying deployments.
  • Use structured JSONReturn consistent JSON so monitoring tools can parse component statuses.
  • Separate liveness from readinessA liveness check confirms the process is running. A readiness check confirms all dependencies are available. Consider having both.

Related Articles