Any midsize company running several internal web services — an ERP frontend, ticket system, monitoring, Confluence, Nextcloud — sooner or later hits the same question: how do you publish those applications under proper hostnames without binding a separate public IP address for every service? The elegant answer is called a reverse proxy. And if your firewall happens to be an OPNsense, the plugin ecosystem around HAProxy and the ACME client delivers exactly the tools you need — including fully automated Lets Encrypt certificates for any number of domains.
In this article we walk you through building a production-ready multi-domain reverse proxy on OPNsense 25.1: with SNI-based routing, health checks for multiple backend pools, automated certificate renewal and the important choice between HTTP-01 and DNS-01 challenges.
Architecture: One public IP, many backends
The basic idea is quickly explained: HAProxy listens on port 443 (and optionally 80 for redirects) of your OPNsense WAN interface. Based on the SNI header in the TLS handshake — meaning the hostname the browser sends along with the connection — HAProxy decides which internal backend the request gets routed to. TLS termination happens directly on the OPNsense, so the internal servers do not need to know anything about certificates.
A typical setup for a midsize business looks like this:
| Public hostname | Internal backend | Purpose |
|---|---|---|
| collab.company.com | 10.20.10.15:8080 | Nextcloud |
| helpdesk.company.com | 10.20.10.22:80 | Zammad |
| monitor.company.com | 10.20.10.31:3000 | Grafana |
| wiki.company.com | 10.20.10.40:8090 | BookStack |
| portal.company.com | 10.20.10.55:443 | ERP web frontend |
All five services share a single public IP. Each service gets its own Lets Encrypt certificate — renewed fully automatically before it expires. Maintenance effort: close to zero once the setup has been cleanly configured.
Install plugins and base configuration
Start under System -> Firmware -> Plugins by installing the two packages os-haproxy and os-acme-client. After installation you will find the new menu entries under Services -> HAProxy and Services -> ACME Client.
Enable HAProxy globally via Services -> HAProxy -> Settings -> Service -> Enable HAProxy. Under Settings -> Global Parameters it is worth setting the default timeouts to practical values — 30 seconds connect, 60 seconds client, 60 seconds server. Do not enable Send proxy protocol globally, only on backends that actually support it.
You then need a firewall rule that allows ports 80 and 443 from WAN to the OPNsense itself. Make sure there is no NAT port forward pointing those ports at an internal server — HAProxy binds the ports directly.
ACME client: HTTP-01 or DNS-01?
When choosing the challenge type you face a very practical decision. The HTTP-01 challenge is simpler: Lets Encrypt calls http://hostname/.well-known/acme-challenge/..., the ACME client stores a file there and HAProxy forwards the request to an internal validation listener. Requirement: the hostname has to be reachable from the outside on port 80.
The DNS-01 challenge, on the other hand, works even for purely internal services whose hostnames you never want to expose externally. The ACME client creates a TXT record in DNS, Lets Encrypt validates it — done. Requirement is an API at your DNS provider. Cloudflare, Hetzner, INWX, DeSEC and many others are supported out of the box.
Our recommendation for everyday practice:
- HTTP-01 for any service that is meant to be reachable publicly anyway (helpdesk portal, customer login, public wiki).
- DNS-01 for internal services with a wildcard certificate (
*.internal.company.com) where you work with split DNS and the hostnames should not even resolve externally.
A clean separation creates clarity and reduces your attack surface. You can read more about the strategic design of your firewall on our OPNsense consulting page.
Configure HAProxy: backends, pools and frontend
The HAProxy configuration in OPNsense is split into several building blocks — the order in which you create them matters so you can pick them in later steps:
- Real Servers define the actual backend hosts with IP and port.
- Backend Pools bundle one or more real servers including health check logic.
- Public Services are the frontend listeners on WAN, this is where TLS termination and SNI routing happen.
- Conditions and Rules map SNI hostnames to the right backend pools.
For the real server hosting Nextcloud this could look like this: name nextcloud-srv, FQDN or IP 10.20.10.15, port 8080, verify SSL certificate disabled, mode active. The backend pool nextcloud-pool references this server, enables health checking with the health check HTTP /status.php, expects HTTP 200 and probes every 5 seconds.
In the public service frontend-tls you create the listener on WAN_IP:443 with SSL offloading enabled, pick the ACME certificates and set Default Backend = nextcloud-pool as a fallback. The SNI branching happens through conditions:
condition: sni-collab -> Host SNI startsWith collab.company.com
condition: sni-helpdesk -> Host SNI startsWith helpdesk.company.com
condition: sni-monitor -> Host SNI startsWith monitor.company.com
rule: route-collab if sni-collab -> use_backend nextcloud-pool
rule: route-helpdesk if sni-helpdesk -> use_backend zammad-pool
rule: route-monitor if sni-monitor -> use_backend grafana-pool
The rules are evaluated in the order in which you attach them to the public service. An additional public service on port 80 with a single rule redirect scheme https code 301 if !{ ssl_fc } makes sure HTTP requests are automatically upgraded to HTTPS.
Automate certificates and wire them up
In the ACME plugin you first create an account at Lets Encrypt (production or staging for testing). You then define your challenge profiles under Validations — for example http-01-haproxy in mode HAProxy Integration or dns-01-cloudflare with your API token. Per certificate you create an entry under Certificates, assign account and validation to it and enter the desired domains. Wildcards like *.internal.company.com are only possible with DNS-01.
Under Automations you can have HAProxy automatically reload after every successful renewal. Set the Automation Restart HAProxy as an action on every certificate — without this step HAProxy would see the new certificate on disk but would only pick it up after a manual restart.
Test the setup in staging mode before switching to production — Lets Encrypt has strict rate limits, and a misconfigured setup can lock you out for a week. The logs under Services -> ACME Client -> Log Files and Services -> HAProxy -> Statistics give you full visibility on every issuance and every request.
Health checks, logging and monitoring
In production, health checks are your safety net. If a backend fails, HAProxy marks it as DOWN and removes it from the pool — with multiple real servers per pool the service keeps running seamlessly. Enable the built-in stats interface under Services -> HAProxy -> Settings -> Service -> Statistics on an internal port and protect it with HTTP basic auth. The dashboard shows latencies, session counts and health status in real time.
In addition we recommend shipping HAProxy logs to your central syslog server and analyzing them in Grafana. A sensible extension is passive monitoring with Zabbix or Checkmk that polls the stats endpoint and alerts on DOWN states. If you run Loki and Promtail, you can filter by frontend and backend labels and quickly spot bottlenecks.
Conclusion and next steps
A central reverse proxy on OPNsense is one of the most rewarding investments in an SMB environment: you reduce complexity, save public IPs, unify TLS standards and gain a clear audit point for every inbound web connection. The combination of HAProxy, ACME client and Lets Encrypt has been battle-tested in OPNsense for years and is rock solid in production.
DATAZONE supports you from architecture planning and the migration of existing reverse proxy setups all the way to day-to-day operations — including monitoring, backup strategy and penetration testing. Reach out via our contact form if you want to put your web publishing on a clean, secure and automated foundation.
More on these topics:
More articles
OPNsense 26.7 Release: What's Coming
OPNsense 26.7 is due: what to expect from the traditional July major release — HardenedBSD/FreeBSD update, plugin refresh, GUI improvements. An honest look at the public roadmap.
WireGuard Site-to-Site VPN in 30 Minutes
Concrete tutorial — connect two sites (head office and branch) via WireGuard on OPNsense. Generate key pairs, configure peers, set allowed IPs correctly, persistent keepalive for NAT traversal, firewall and routing rules. Production-ready in 30 minutes.
OPNsense Multi-WAN: Failover Done Right for SMB
Configure two WAN connections in OPNsense — fibre and LTE backup — as a failover for the mid-market. Gateway groups, Tier 1/2, health checks via monitor IP, sticky connections for VoIP, outbound NAT per WAN. Why true failover beats load balancing for most SMBs.