← Back to Article List         
Health Checks in ASP.NET Core Web API

Health Checks in ASP.NET Core Web API

Published on 26 Sep 2026     7 min read Web API
Web API

1. What is a health check?

A health check is an endpoint that reports whether your application is running and whether its configured dependencies are working.

Example:

GET /health

Response:

Healthy

The basic health check only confirms that the application can respond. Checking SQL Server, Redis, or another API requires additional checks. Microsoft Learn

2. Why do we need it?

An application might be running while its database is unavailable.

Health checks help monitoring tools detect these problems. Depending on how your hosting platform is configured, it can:

  • Alert the support team.
  • Stop sending traffic to an unhealthy instance.
  • Restart an unresponsive application.

3. Full simple program

For a basic health check, no additional NuGet package or controller is required in a standard ASP.NET Core Web API project.

Use this Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// Register health check services.
builder.Services.AddHealthChecks();

var app = builder.Build();

app.UseHttpsRedirection();

// Create the health check endpoint.
app.MapHealthChecks("/health");

app.MapControllers();

app.Run();

Run the application and open this URL in a browser or Postman:

GET https://localhost:7001/health

Replace 7001 with your application's HTTPS port.

Response:

200 OK
Healthy

4. Code explanation

builder.Services.AddHealthChecks();

Registers the services required to run health checks.

app.MapHealthChecks("/health");

Creates the /health endpoint. Calling it runs the registered checks and returns the overall status.

These are the two essential lines for a basic health endpoint. Microsoft Learn

What happens if the application stops?

There will be no health response—the monitoring tool sees a connection failure or timeout. A stopped application cannot return Unhealthy.

5. How do we check a dependency?

Implement IHealthCheck and register it using AddCheck<T>().

For example, a database check would attempt to connect to the database and return:

return HealthCheckResult.Healthy("Database is available.");

Or, if the check fails:

return HealthCheckResult.Unhealthy("Database is unavailable.");

The actual connection test must be implemented; returning Healthy() by itself does not test the database.

ASP.NET Core supports these statuses and default HTTP responses: GitHub

Status Meaning Default HTTP status
Healthy Registered checks passed 200
Degraded A check reports reduced functionality 200
Unhealthy A check reports failure 503

6. Advantages

  • Simple to add.
  • Provides a dedicated monitoring endpoint.
  • Can check dependencies such as databases and caches.
  • Helps hosting platforms make traffic-routing decisions.
  • Makes availability problems easier to detect.

7. Key points

  • AddHealthChecks() registers the services.
  • MapHealthChecks("/health") exposes the endpoint.
  • The basic check does not automatically test SQL Server or Redis.
  • Liveness: Can the application respond?
  • Readiness: Is it ready to serve requests, including required dependencies?
  • Health checks report status; monitoring or hosting tools perform alerts and recovery.
  • Keep checks lightweight and avoid exposing sensitive error details.
  • If you add this to the API key example above, its middleware also protects /health. The monitoring client must send the key unless you explicitly exempt that endpoint.

Checking a dependency means performing a small operation against a service your API relies on to confirm that it is available.

For example:

Dependency Simple check
SQL Server Open a connection and execute SELECT 1
Redis Send a PING
Another Web API Call its health endpoint
File storage Check access to the required location

Let’s implement a SQL Server health check using a simple custom class.

1. What will our program check?

When someone calls /health, the API will:

  1. Connect to the configured SQL Server database.
  2. Execute SELECT 1.
  3. Return Healthy if successful.
  4. Return Unhealthy if the connection or query fails.

SELECT 1 is a lightweight query that returns the number 1 without reading an application table. Microsoft documents this as an example of a small database probe. Microsoft Learn

2. Install the package

Using the terminal:

dotnet add package Microsoft.Data.SqlClient

Or Visual Studio’s Package Manager Console:

Install-Package Microsoft.Data.SqlClient

This package provides SqlConnection and SqlCommand for communicating with SQL Server.

3. Add the connection string

appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=HealthCheckDemo;Trusted_Connection=True;Encrypt=True;TrustServerCertificate=True;Connect Timeout=5"
  }
}

Update the server and database names to match your environment. The database must already exist.

Setting Purpose
Server SQL Server instance
Database Database to check
Trusted_Connection=True Uses Windows authentication
Connect Timeout=5 Limits the connection wait to five seconds
TrustServerCertificate=True Allows a local development certificate without validation

Use a properly trusted SQL Server certificate in production.

4. Create DatabaseHealthCheck.cs

Create a HealthChecks folder and add:

using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace HealthCheckDemo.HealthChecks;

public class DatabaseHealthCheck : IHealthCheck
{
    private readonly IConfiguration _configuration;

    public DatabaseHealthCheck(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        var connectionString =
            _configuration.GetConnectionString("DefaultConnection");

        if (string.IsNullOrWhiteSpace(connectionString))
        {
            return HealthCheckResult.Unhealthy(
                "Database connection string is missing.");
        }

        try
        {
            // Connect to the configured database.
            using var connection = new SqlConnection(connectionString);

            await connection.OpenAsync(cancellationToken);

            // Execute a lightweight query.
            using var command = new SqlCommand("SELECT 1", connection);

            command.CommandTimeout = 5;

            await command.ExecuteScalarAsync(cancellationToken);

            return HealthCheckResult.Healthy(
                "Database connection and query succeeded.");
        }
        catch (OperationCanceledException)
            when (cancellationToken.IsCancellationRequested)
        {
            throw;
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy(
                "Database check failed.",
                exception: ex);
        }
    }
}

Replace HealthCheckDemo with your project namespace.

5. Register it in Program.cs

using HealthCheckDemo.HealthChecks;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

// Register our SQL Server health check.
builder.Services.AddHealthChecks()
    .AddCheck<DatabaseHealthCheck>("sql_server");

var app = builder.Build();

app.UseHttpsRedirection();

// Calling this endpoint executes DatabaseHealthCheck.
app.MapHealthChecks("/health");

app.MapControllers();

app.Run();

No health check controller is required. ASP.NET Core calls your IHealthCheck implementation through the mapped endpoint. GitHub

6. Explanation of the important code

IHealthCheck — the contract

public class DatabaseHealthCheck : IHealthCheck

This interface tells ASP.NET Core that the class implements a health check.

It requires this method:

CheckHealthAsync(...)

You put the actual dependency test inside it.

OpenAsync() — verify connectivity

await connection.OpenAsync(cancellationToken);

Attempts to connect using the configured server, database, and credentials. It can fail when the server is unreachable, authentication fails, or the database is unavailable. Microsoft Learn

ExecuteScalarAsync() — verify query execution

await command.ExecuteScalarAsync(cancellationToken);

Executes SELECT 1, which returns one value. Successful execution confirms that this connection can run a simple query. Microsoft Learn

HealthCheckResult — report the result

return HealthCheckResult.Healthy("...");

Reports that the check passed.

return HealthCheckResult.Unhealthy("...", exception: ex);

Reports that the check failed and attaches the exception for diagnostics. The default response does not expose that exception to the caller.

AddCheck() — register our class

.AddCheck<DatabaseHealthCheck>("sql_server");
  • DatabaseHealthCheck is the class to execute.
  • "sql_server" is the name identifying this check in the health report.

using — release resources

using var connection = new SqlConnection(connectionString);

Disposes the connection when the method finishes, including when an exception occurs.

7. Test the program

Run the API and call:

GET https://localhost:7001/health

Replace the port with your application’s HTTPS port.

Scenario HTTP response Body
Database connection and query succeed 200 OK Healthy
Database connection or query fails 503 Service Unavailable Unhealthy

To test failure safely, temporarily change the database name in your local configuration to one that does not exist, then restart and call /health.

Why don’t we see “Database check failed” in the response?

The default health response displays only the overall status. To display individual check names and descriptions, replace:

app.MapHealthChecks("/health");

with:

app.MapHealthChecks("/health",
    new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
    {
        ResponseWriter = async (context, report) =>
        {
            await context.Response.WriteAsJsonAsync(new
            {
                status = report.Status.ToString(),

                checks = report.Entries.Select(entry => new
                {
                    name = entry.Key,
                    status = entry.Value.Status.ToString(),
                    description = entry.Value.Description
                })
            });
        }
    });

Successful response:

{
  "status": "Healthy",
  "checks": [
    {
      "name": "sql_server",
      "status": "Healthy",
      "description": "Database connection and query succeeded."
    }
  ]
}

The framework still supplies the appropriate HTTP status code.

8. Key points

  • The dependency check runs when /health is called. This example does not schedule background checks.
  • A monitoring system must call the endpoint periodically.
  • SELECT 1 verifies connectivity and simple query execution; it does not verify your tables, stored procedures, or business logic.
  • Keep checks lightweight and use short timeouts.
  • Database availability usually belongs in a readiness check.
  • Keep liveness independent of database availability so a database outage does not cause unnecessary application restarts.
  • If several checks are registered, /health runs them and reports their combined status.
  • Avoid returning connection strings or raw exception details in a public health response.