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.
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:
- 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).
- 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.
- Automations → Add: create an automation that restarts HAProxy after a certificate is issued or renewed, so the new cert is loaded without manual intervention.
- Certificates → Add: enter your hostname(s) (e.g.,
jellyfin.example.com, or a wildcard*.example.comwith 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
- Service is up: Services → HAProxy → the Stats page (enable the stats listener if you want it) shows each backend pool green/UP.
- Certificate is valid: browse to
https://jellyfin.example.comand confirm the padlock shows a Let’s Encrypt cert with the right name — no warning. - 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
OPNsense Multi-WAN Failover with Gateway Groups
Configure two ISP uplinks on OPNsense with gateway groups for automatic failover or load balancing: monitoring, NAT, DNS, and pitfalls that break it.
OPNsense VLAN Configuration: Segment IoT, Guest, and Trusted
How to create and enforce VLANs on OPNsense to isolate IoT devices, guest Wi-Fi, and your trusted LAN — with firewall rules that block inter-VLAN traffic by default.
Zenarmor (Sensei) on OPNsense: Free NGFW Setup and Tuning
Install and configure Zenarmor (formerly Sensei) on OPNsense for application-aware filtering, web category blocking, and live traffic analytics — what the Free edition does, and where the paid tiers start.