Designing a Zero-Drama HTTPS Maintenance Server with NGINX and Cloudflare

Overview

When production servers need to be taken completely offline, the goal is not just uptime messaging. It is trust. Visitors should see a calm, intentional maintenance page over HTTPS, not browser warnings, Cloudflare error screens, or raw 503 output.

This article walks through a practical pattern for building a dedicated NGINX maintenance server that can safely replace production during maintenance windows using NAT, while remaining compatible with HSTS and Cloudflare.

Requirements

  • HTTPS-only (no port 80)
  • Compatible with HSTS
  • Works with Cloudflare (proxied or DNS-only)
  • Supports multiple unrelated domains
  • No dependency on production NGINX configs
  • Predictable behavior during outages

Architectural Approach

Separate Responsibilities

  • Production servers route traffic to applications
  • Maintenance server only terminates TLS and returns a maintenance response

This separation avoids configuration drift and reduces risk during outages. The maintenance server never needs to know how applications work.

Why You Cannot Use a “Wildcard TLS Handler”

TLS certificates are selected before NGINX request logic runs. Because of this:

  • Certificates must be declared explicitly
  • A single wildcard certificate cannot span unrelated domains
  • NGINX cannot dynamically choose certificates at runtime

The correct approach is one wildcard certificate per apex domain, selected via SNI.

Certificate Strategy (DNS-01)

Using Cloudflare’s DNS API allows certificates to be issued without any HTTP endpoints:

  • One wildcard certificate per domain
  • No HTTP challenges
  • No dependency on running services
  • Fully HSTS-safe
certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d domain.com \
  -d '*.domain.com'

NGINX Maintenance Configuration

Key Design Points

  • Always return HTTP 503
  • Serve a custom HTML page
  • No proxying
  • No application logic
  • Identical behavior for all paths
server {
    listen 443 ssl;
    server_name domain.com *.domain.com;

    ssl_certificate     /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;

    root /var/www/maintenance;
    index index.html;

    error_page 503 =503 /index.html;

    location / {
        add_header Retry-After 300 always;
        add_header Cache-Control "no-store, no-cache, must-revalidate" always;
        return 503;
    }
}

NAT Failover Model

During maintenance:

  • The public IP address remains unchanged
  • NAT forwards port 443 to the maintenance server
  • Cloudflare continues to function normally

This avoids DNS changes and eliminates propagation delays.

Common Pitfalls (and Fixes)

Host Firewall Blocking HTTPS

firewall-cmd --add-service=https --permanent
firewall-cmd --reload

SELinux Blocking Static Files

On RHEL-family systems, NGINX requires the correct SELinux context.

semanage fcontext -a -t httpd_sys_content_t "/var/www/maintenance(/.*)?"
restorecon -Rv /var/www/maintenance

Without this, NGINX will return 403 even when filesystem permissions are correct.

Testing Without Impacting Production

You can validate the maintenance server without touching DNS:

curl -I --resolve domain.com:443:10.0.0.10 https://domain.com

This confirms TLS, certificate selection, HTTP status, and content.

Synchronization Strategy

Instead of syncing full NGINX configurations:

  • Maintain a simple domain list
  • Issue certificates only for domains that may fail over
  • Generate maintenance configs from a template

This keeps the system predictable and auditable.

Final Result

  • Calm, branded maintenance pages
  • Correct HTTP semantics
  • No browser warnings
  • No Cloudflare error pages
  • Minimal moving parts

Most importantly, maintenance feels intentional rather than broken.

Posted in

Jakob Ward

Jakob Ward is a multidisciplinary entrepreneur with a focus on technology and music. He develops custom web applications, optimizes hosting infrastructures, and creates integrated digital platforms under E19 Creative. Jakob is also an independent musician, building brands like Centris Music and Studio Host to support creative communities. Passionate about clean code, sustainable design, and meaningful experiences, Jakob bridges technical expertise with artistic vision.