Web Server Availability Monitoring: Nginx, Apache, and Caddy Uptime Best Practices
Executing systemctl is-active nginx or verifying that port 80 accepts a TCP socket connection does not prove that a web server is available to end users. A web server process can be active while worker thread pools are saturated, upstream reverse-proxy sockets are timing out with HTTP 504 errors, TLS certificates are expired, or Anycast DNS routes are black-holed.
Site Reliability Engineers must monitor web server availability across multiple operational layers: process lifecycle, socket connection queues, TLS negotiation, HTTP semantics, upstream application health, and external multi-region reachability.
External Multi-Region Synthetic Probes (Quorum Verification)
│
├── 1. Authoritative DNS Lookup (A / AAAA Dual-Stack)
│
├── 2. TCP Handshake (:80 / :443 SYN-ACK Timing)
│
├── 3. TLS 1.3 Handshake (SNI & ALPN Negotiation)
│
▼
Web Server Edge Layer (Nginx / Apache / Caddy)
│
├── Process & Worker Pool Saturation (stub_status / mod_status / metrics)
│
▼
Reverse Proxy Upstream Layer (HTTP 502 / 504 Detection)
│
├── Upstream Connect Time (Unix Socket / Private VPC IP)
├── Upstream Response Time (Node.js / Python / Go / PHP-FPM)
│
▼
Deep Health Check Endpoint (/healthz with JSON Schema Validation)
1. Defining True Web Server Availability
In high-throughput environments, availability is a continuum rather than a binary up/down state. SREs classify availability across five distinct operational boundaries:
1. Process Availability ──► systemd unit running (systemctl is-active caddy == active)
2. Socket Availability ──► Kernel accepts TCP SYN on port 443 without backlog drops
3. TLS Availability ──► Valid X.509 certificate served with negotiated ALPN (h2 / http/1.1)
4. HTTP Gateway Health ──► Web server returns valid HTTP status (excludes 502/503/504)
5. Application Correctness──► Response body matches expected semantic schema within latency budget
The Availability Calculation Formula
Calculate request-based availability over defined observation windows to determine error budget consumption:
$$ \text{Availability}_{\text{req}} = \frac{\sum \text{Valid 2xx / 3xx Responses}}{\sum \text{Total Ingress Requests} - \text{Client Errors (4xx)}} \times 100 $$
Target SLO: 99.95% Availability
Monthly Error Budget: 0.05% of Total Requests (Approx. 21m 36s of Allowable Downtime)
Need to calculate your error budget or convert uptime nines into allowable downtime? Use our interactive SLA Calculator to model monthly and annual availability targets.
2. Deep Web Server Health Check Design
A production health check must validate end-to-end functionality without placing synchronous query load on backend databases.
GET /healthz ──► Lightweight In-Memory Check (Process Alive & Upstream Socket Reachable)
GET /readyz ──► Deep Dependency Check (Cache / Database Read-Only Ping)
Comprehensive cURL Diagnostic Command
Use cURL with formatted timing variables to dissect DNS resolution, TCP connect, TLS handshake, and Time to First Byte (TTFB):
curl --fail \
--silent \
--show-error \
--location \
--connect-timeout 3 \
--max-time 10 \
--write-out '\n--- Timing & Status Breakdown ---\nHTTP Status: %{http_code}\nDNS Lookup: %{time_namelookup}s\nTCP Connect: %{time_connect}s\nTLS Handshake: %{time_appconnect}s\nPre-Transfer: %{time_pretransfer}s\nStart Transfer: %{time_starttransfer}s (TTFB)\nTotal Duration: %{time_total}s\n' \
https://example.com/healthz
3. Nginx Availability and Saturation Monitoring
Nginx uses an event-driven, non-blocking asynchronous architecture. When Nginx fails, it typically stems from worker connection exhaustion or upstream gateway timeouts.
3.1 Enabling Nginx stub_status
Configure an internal-only status endpoint on 127.0.0.1:
# /etc/nginx/conf.d/stub_status.conf
server {
listen 127.0.0.1:8080;
server_name 127.0.0.1;
location = /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
access_log off;
}
}
# Query Nginx internal connection counters
curl -s http://127.0.0.1:8080/nginx_status
Active connections: 290
server accepts handled requests
142095 142095 385012
Reading: 0 Writing: 14 Waiting: 276
Active connections: Current open client connections.Waiting: Keep-alive connections waiting for subsequent requests. HighWaitingwith lowWritingindicates idle keep-alive connections consuming memory.Handled < Accepts: Indicates Nginx dropped incoming TCP connections due toworker_connectionsor file-descriptor limits.
3.2 High-Value Nginx Upstream Logging
Add upstream timing variables to the Nginx log_format in /etc/nginx/nginx.conf:
log_format upstream_timed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
uct(upstream_connect_time): Time taken to establish a TCP/Unix socket connection to the backend application worker.urt(upstream_response_time): Time taken for the backend application to generate and return the response body.
4. Apache HTTP Server Availability Monitoring
Apache relies on Multi-Processing Modules (MPMs): event, worker, or legacy prefork.
4.1 Enabling Apache mod_status
Enable mod_status with extended metrics in /etc/apache2/mods-available/status.conf:
<IfModule mod_status.c>
ExtendedStatus On
<Location /server-status>
SetHandler server-status
Require ip 127.0.0.1 ::1
</Location>
</IfModule>
# Fetch machine-readable Apache metrics
curl -s "http://127.0.0.1/server-status?auto"
Total Accesses: 492019
Total kBytes: 1048576
BusyWorkers: 42
IdleWorkers: 86
Scoreboard: __________________WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW...
- BusyWorkers vs. IdleWorkers: When
IdleWorkers == 0, Apache has reachedMaxRequestWorkers. Incoming TCP connections will queue in the OS socket backlog until timeout.
5. Caddy Availability Monitoring
Caddy provides native Prometheus metrics and a dynamic JSON Admin API over a local Unix domain socket or loopback TCP port (127.0.0.1:2019).
5.1 Querying Caddy Native Metrics
# Scrape Caddy native Prometheus telemetry directly
curl -s http://127.0.0.1:2019/metrics | grep -E 'caddy_http_requests_total|caddy_http_request_duration_seconds'
5.2 Automatic HTTPS & ACME Renewal Monitoring
Caddy manages Let's Encrypt / ZeroSSL certificate issuance automatically. Monitor certificate renewal status by checking Caddy's internal storage and logs:
# Check Caddy certificate expiration and internal ACME state
journalctl -u caddy --since "1 hour ago" | grep -Ei 'certificate|acme|renew|error'
Verifying SSL/TLS certificates and expiration on your live endpoints? Use our SSL Inspector Tool to validate certificate chains and expiration dates.
6. Web Server Comparison: Availability & Saturation
| Metric / Capability | Nginx | Apache HTTP Server | Caddy |
|---|---|---|---|
| Architecture | Event-driven non-blocking async | Multi-process / Multi-threaded MPM | Goroutine-per-connection async |
| Telemetry Interface | stub_status / Access logs | mod_status?auto | Native /metrics on Admin API |
| Saturation Metric | Active connections vs worker_connections | BusyWorkers vs MaxRequestWorkers | In-flight HTTP request gauges |
| Gateway Timeout Signal | HTTP 504 (upstream_response_time) | HTTP 504 (proxy:error) | HTTP 504 (reverse_proxy timeout) |
| TLS Management | External (Certbot / cert-manager) | External (Certbot / ACME client) | Native automated ACME engine |
| Primary Failure State | Worker connection pool saturation | Thread / Process pool starvation | Upstream reverse proxy dial timeout |
7. Prometheus Alerting Rules for Web Servers
Implement PromQL alerting rules to detect gateway errors, latency spikes, and socket drops before user impact escalates.
7.1 Web Server High 5xx Gateway Error Rate (> 1%)
(
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
) * 100 > 1.0
7.2 Nginx Worker Connection Saturation (> 85%)
(
nginx_connections_active
/
(nginx_worker_processes * nginx_worker_connections)
) * 100 > 85.0
7.3 Apache MPM Worker Pool Starvation (Zero Idle Workers)
apache_workers{state="idle"} == 0
8. SRE Web Server Health Threshold Matrix
| Operational Signal | Healthy Baseline | Warning Signal | Critical (Page On-Call) | SRE Immediate Action |
|---|---|---|---|---|
| 5xx Error Rate | $< 0.05%$ | $0.2% - 1.0%$ | $> 1.0%$ | Check upstream application workers |
Upstream Connect (uct) | $< 2\text{ ms}$ | $10 - 50\text{ ms}$ | $> 100\text{ ms}$ | App worker socket backlog exhausted |
Upstream Response (urt) | $< 150\text{ ms}$ | $300 - 800\text{ ms}$ | $> 1500\text{ ms}$ | Inspect database queries & locks |
| Worker Utilization | $< 60%$ | $75% - 85%$ | $> 90%$ | Scale worker count or increase proxy replicas |
| TLS Certificate Expiry | $> 30\text{ days}$ | $7 - 14\text{ days}$ | $< 72\text{ hours}$ | Execute manual certificate rotation |
| TCP SYN Drops | $0\text{ drops}$ | $> 5\text{ / min}$ | $> 50\text{ / min}$ | Increase somaxconn and listen backlog |
9. Troubleshooting Runbook: Resolving Web Server Outages
When external monitoring reports an outage or HTTP 5xx errors spike, execute this twelve-step diagnostic sequence:
- Verify reachability from at least three external geographic vantage points to rule out local transit issues.
- Inspect DNS resolution across IPv4 and IPv6 using
dig +short A example.comanddig +short AAAA example.com. - Test direct port reachability using
curl -Iv --connect-timeout 3 https://example.com/healthz. - Validate SSL/TLS certificate chains and cipher negotiation using
openssl s_client -connect example.com:443 -servername example.com. - Check web server process status and recent crash logs using
systemctl status nginx(orapache2,caddy) andjournalctl -xeu <service>. - Query internal telemetry (
/nginx_status,/server-status?auto,/metrics) to check active worker saturation. - Examine access and error logs for HTTP 502 (Bad Gateway) vs HTTP 504 (Gateway Timeout) signatures.
- Inspect upstream application sockets; verify whether backend workers (PHP-FPM, Gunicorn, Puma, Node.js) are alive and listening.
- Audit Linux system file-descriptor limits using
cat /proc/sys/fs/file-nrandulimit -n. - Check socket listen overflow drops using
nstat -az | grep -i listen. - Restart or gracefully reload the web server configuration (
nginx -s reload/systemctl reload apache2/caddy reload). - Calculate error budget consumption using the Downtime Calculator to determine SLA compliance.
10. Engineering Implementation Checklist
- Configure Deep Health Endpoints: Expose
/healthz(liveness) and/readyz(readiness) with JSON semantic output. - Enable Status Modules: Securely configure
stub_status(Nginx),mod_status(Apache), or/metrics(Caddy) on loopback interfaces only. - Log Upstream Response Times: Capture
upstream_connect_timeandupstream_response_timein access logs. - Tune OS Socket Backlogs: Set
/proc/sys/net/core/somaxconnto at least4096and match Nginxlisten 443 backlog=4096. - Deploy Multi-Region Synthetic Probes: Run external black-box availability probes every 30–60 seconds across multiple cloud regions.
- Implement Rate-Based 5xx Alerts: Configure Prometheus alerts on 5xx error rates (> 1%) rather than static counts.
- Automate TLS Expiry Monitoring: Alert on certificate expiration with warning at 14 days and critical at 72 hours.
- Verify Dual-Stack IPv4 / IPv6: Test that web servers answer identically on both A and AAAA records.
Related Infrastructure & SRE Runbooks
When troubleshooting web server downtime and upstream proxy degradation:
- To identify whether CPU, memory, or socket starvation is freezing worker pools, read Linux server resource saturation.
- For step-by-step triage of upstream 502/504 errors, follow our HTTP 5xx server error diagnosis runbook.
- To prevent certificate expiry from breaking HTTPS virtual hosts, configure automated SSL/TLS certificate monitoring.
Stop Finding Out About Outages from Angry Users
Get instant WhatsApp & Discord alerts the second your API, website, or server goes down. Setup in 30 seconds with 60-second checks.