Post

Nginx Proxy Manager: SSL & Service Routing

Deploying Nginx Proxy Manager with a wildcard SSL certificate via Cloudflare DNS challenge.

Nginx Proxy Manager: SSL & Service Routing

Overview

With Pi-hole resolving all lab subdomains to a single IP address, the next component was something to sit at that IP and intelligently route requests to the correct backend service. That component is Nginx Proxy Manager (NPM), a reverse proxy with a web UI that handles SSL termination, automatic certificate renewal, and hostname-based routing.

This post covers the NPM deployment, wildcard SSL certificate generation via Cloudflare DNS challenge, and proxy host configuration for every lab service.


What a Reverse Proxy Does

Without a reverse proxy, accessing a service means remembering specific IP and port combinations: https://192.168.20.XX:8006 for Proxmox, http://192.168.20.XX:80 for Pi-hole, and so on. This approach has two significant problems:

  1. Usability: IP:PORT combinations are difficult to remember and share
  2. Security: there is no clean mechanism to deploy valid SSL certificates on each individual service

A reverse proxy solves both. It accepts requests by hostname, matches them to configured backends, and forwards traffic transparently. The client only communicates with NPM; backend IPs and ports remain internal.

The SSL certificate lives on NPM, one certificate, one renewal process, all services covered.

[PLACEHOLDER: Diagram showing client request flow: Browser → DNS (Pi-hole) → NPM → Backend Service, with SSL termination happening at the NPM layer] NPM Client Request Diagram NPM Client Request Diagram


Deployment

NPM runs as an LXC container on pavilion, installed via the Proxmox VE Helper Scripts.

SettingValue
CT ID101
Base ImageDebian (via helper script)
VLAN20 — Lab
IP192.168.20.XX/24
DNSPi-hole

After installation, the NPM admin panel is accessible at http://192.168.20.XX:81.


Wildcard SSL Certificate

Rather than generating individual certificates per service, a single wildcard certificate covers every subdomain under *.jamcre.dev, one cert, automatic renewal, zero per-service configuration.

How It Works

Let’s Encrypt issues wildcard certificates exclusively via DNS challenge, which proves domain ownership by creating a DNS TXT record. Since Cloudflare manages the jamcre.dev domain, NPM’s built-in Cloudflare integration handles this automatically.

Configuration Steps

1. Create a Cloudflare API Token

In the Cloudflare dashboard, create an API token with Edit Zone DNS permissions scoped to jamcre.dev. This is the minimum permission required.

Do not use the global API key. A scoped token follows the principle of least privilege.

2. Request the Certificate in NPM

Navigate to SSL Certificates → Add SSL Certificate → Let’s Encrypt:

FieldValue
Domain*.jamcre.dev
DNS ProviderCloudflare
Credentialsdns_cloudflare_api_token=<token>
Propagation seconds0

3. Fix the Admin Email (Critical)

The NPM helper script sets the default admin email to [email protected]. Let’s Encrypt rejects this address during account registration, causing the certificate request to fail silently.

Update the NPM admin account email to a valid address before requesting the certificate.

Once issued, NPM renews the certificate automatically before expiry with no manual intervention.

Wildcard certificate active Wildcard certificate for *.jamcre.dev issued and active


Proxy Host Configuration

With the certificate in place, each service receives a proxy host entry. All hosts have Force SSL, HSTS, and HTTP/2 enabled.

SourceBackendNotes
proxmox.jamcre.devhttps://192.168.20.XX:8006WebSockets enabled
pihole.jamcre.devhttp://192.168.20.XX:80
nginx.jamcre.devhttp://192.168.20.XX:81NPM proxying itself
wazuh.jamcre.devhttps://192.168.20.XX:443proxy_ssl_verify off, WebSockets

Notable Configurations

Wazuh — uses proxy_ssl_verify off because its backend serves a self-signed certificate that NPM cannot verify. The client-to-NPM connection remains fully encrypted via the wildcard cert.

NPM proxying itselfnginx.jamcre.dev routes to NPM’s own admin port. This sounds circular but functions correctly, providing a clean HTTPS URL for the admin panel.

All proxy hosts configured All proxy hosts configured and active in NPM


What’s Next

Every lab service now has a clean HTTPS hostname accessible from VLAN 10/20. The wildcard certificate handles all subdomains automatically. The infrastructure layer of V2 is complete.

Next: Wazuh SIEM, the service I’ve been looking forward to deploying.

This post is licensed under CC BY 4.0 by the author.