OPNsenseLab
Isometric vector illustration showing OPNsense firewall with HAProxy and Let's Encrypt SSL integration for secure reverse proxy setup.
Networking

HAProxy Reverse Proxy on OPNsense with Let's Encrypt

Use the os-haproxy and os-acme-client plugins to put one HTTPS front end in front of all your homelab services on OPNsense — real backends, SNI host routing, and automatic Let's Encrypt certificates.

By OPNsenseLab Editorial · · 8 min read

A reverse proxy lets you reach many internal services — Jellyfin, a Git server, a dashboard — through one HTTPS endpoint, with the firewall terminating TLS and routing by hostname. OPNsense can do this natively with the os-haproxy plugin, and os-acme-client handles Let’s Encrypt certificates automatically. This guide builds a working host-based router with valid certs.

Before you touch the config, understand the model. HAProxy has frontends (where clients connect — typically :443), backends (your real internal servers), and conditions/rules that map an incoming hostname to a backend. The plugin’s terminology splits these into Real Servers, Backend Pools, Rules, Conditions, and Public Services — it’s more granular than a typical proxy config, and that’s the main thing that trips people up. Build the pieces bottom-up: real server → backend pool → condition → rule → public service (frontend).

One prerequisite decides whether this is even worth it: a resolvable hostname. Let’s Encrypt issues certificates for names, not IPs. You need either a real domain you control or a Dynamic DNS hostname, plus a way to pass the ACME challenge (covered below). If you only ever reach services by IP on the LAN, a reverse proxy with public certs is overkill — a self-signed cert and a bookmark are simpler.

Step 1: Free up port 443 on the firewall

HAProxy’s HTTPS frontend wants TCP 443, but the OPNsense web GUI defaults to it. In System → Settings → Administration:

  • Change the TCP port for the web GUI to something else (e.g., 4443).
  • Disable the HTTP redirect so the GUI isn’t competing for 80 either.
  • Keep Listen interfaces on LAN only — exposing the management GUI is never the goal here.

Re-log in to the GUI on the new port to confirm before continuing.

Step 2: Install the plugins

System → Firmware → Plugins → install os-haproxy and os-acme-client.

After install: HAProxy lives under Services → HAProxy, and the ACME client under Services → ACME Client.

Step 3: Get a Let’s Encrypt certificate with the ACME client

Services → ACME Client:

  1. Accounts → Add: enter your email, choose Let’s Encrypt as the CA (use the staging CA first if you want to test without hitting rate limits).
  2. Challenge Types → Add: the simplest robust option for a homelab is DNS-01 if your DNS provider is supported (Cloudflare, deSEC, etc.) because it works even when 80/443 aren’t publicly reachable and supports wildcard certs. HTTP-01 also works but requires inbound port 80 reaching the firewall, which you then have to hand to HAProxy.
  3. Automations → Add: create an automation that restarts HAProxy after a certificate is issued or renewed, so the new cert is loaded without manual intervention.
  4. Certificates → Add: enter your hostname(s) (e.g., jellyfin.example.com, or a wildcard *.example.com with DNS-01), and attach the account, challenge, and automation. Issue it and confirm it shows a valid not-after date.

Using DNS-01 with a wildcard means you issue one cert and add new subdomains later without re-validating each — the cleanest pattern for a growing homelab.

Step 4: Define the backend (your real service)

Services → HAProxy → Real Servers → Add:

  • Name: jellyfin-server
  • FQDN or IP: the internal IP of the service (e.g., 192.168.10.50)
  • Port: the service’s real port (e.g., 8096)

Then Backend Pools → Add:

  • Name: jellyfin-pool
  • Servers: select jellyfin-server
  • Leave health checking on its sensible default; HAProxy will mark a dead backend down rather than sending traffic into a black hole.

Step 5: Map hostname → backend with a condition and rule

Rules & Checks → Conditions → Add:

  • Type: Host matches (HTTP Host header)
  • Host: jellyfin.example.com

Rules & Checks → Rules → Add:

  • Condition: the host condition above
  • Action: use the specified backend pool → jellyfin-pool

Repeat steps 4–5 per service. Each service is a real server + pool + host condition + rule; the frontend stays shared.

Step 6: Create the public service (frontend)

Virtual Services → Public Services → Add:

  • Listen Address: 0.0.0.0:443 (or your WAN/LAN address as appropriate)
  • Type: HTTP / HTTPS (SSL offloading)
  • Certificates: select the Let’s Encrypt cert from Step 3
  • Select Rules: attach the host rules you created

Optionally add a second frontend on :80 whose only job is to 301-redirect to HTTPS.

Step 7: Enable and apply

Services → HAProxy → Settings → check Enable HAProxy, Save, then Apply. Add a WAN (or LAN) firewall rule permitting TCP 443 to the HAProxy listen address if you’re fronting anything reachable from outside the segment.

Verify it works

  1. Service is up: Services → HAProxy → the Stats page (enable the stats listener if you want it) shows each backend pool green/UP.
  2. Certificate is valid: browse to https://jellyfin.example.com and confirm the padlock shows a Let’s Encrypt cert with the right name — no warning.
  3. Routing is correct: a second hostname (git.example.com) lands on its own backend, not the first one. If everything resolves to one service, your host conditions are wrong or a catch-all rule is ordered above the specific ones.

When HAProxy on OPNsense is the wrong tool

Running the proxy on the firewall couples your service reachability to the firewall’s load — fine for a homelab, less ideal if you’d rather keep the firewall lean, in which case a dedicated Nginx/Caddy/Traefik box is cleaner. If you need a polished UI for managing many vhosts and certs, Nginx Proxy Manager on a small VM is friendlier than HAProxy’s granular plugin. And never expose an internal admin panel through the proxy without authentication in front of it — a reverse proxy publishes services; it doesn’t secure them. For services you genuinely want remote, consider putting them behind the WireGuard road-warrior VPN instead of the public internet.

Comparing approaches? FirewallCompare covers reverse-proxy options on OPNsense vs pfSense. The OPNsense reverse proxy documentation is the authoritative reference for the plugin’s fields.

Related

Comments