HAProxy on OPNsense - configuring your reverse proxy
Part 2 of 3 - Self-Hosting with OPNsense and HAProxy
← Part 1: What is a reverse proxy? Self-hosting explained
Part 3: HTTPS for self-hosted services - ACME, Let’s Encrypt & Cloudflare DNS →
Part 1 of this series covered what a reverse proxy is and why you need one for self-hosting multiple services on a single public IP. This post gets practical: installing HAProxy on OPNsense and configuring it to route incoming requests to the right internal service based on hostname.
By the end of this post you'll have HAProxy running on OPNsense with at least one service accessible via HTTPS. Certificates are covered in Part 3; for now we'll set up the routing structure and test with a self-signed certificate.
How HAProxy thinks about traffic
Before diving into the UI, it helps to understand HAProxy's core concepts:
- Frontend
- The listener. A frontend defines what IP address and port HAProxy accepts connections on, and what to do with them. Your HTTPS frontend will listen on port 443 on your WAN interface.
- Backend
- The destination. A backend defines one or more servers (with IP addresses and ports) that HAProxy can forward traffic to. Each service you host gets its own backend.
- ACL (Access Control List)
- A rule. ACLs let HAProxy match incoming requests against conditions - most commonly the hostname in the request. An ACL rule like “if the hostname is blog.example.com” is what lets HAProxy decide which backend to use.
- Server
- An entry in a backend pointing to a specific host and port. For home self-hosting this is typically a local IP address and port number.
The flow for an HTTPS request looks like this: the frontend receives it on port 443 → ACLs inspect the hostname → traffic is forwarded to the matching backend → the backend sends it to the correct internal server.
Step 1: Install the HAProxy plugin
HAProxy is not installed by default on OPNsense. Install it via the plugin manager:
- Go to System → Firmware → Plugins.
- Search for
os-haproxy. - Click the + button to install it.
- Once installed, HAProxy will appear under Services → HAProxy.
You don't need to restart OPNsense; the plugin is available immediately after installation.
Step 2: Create a backend for each service
Go to Services → HAProxy → Real Servers first, then Backend Pools. The approach is:
-
Create a Real Server for each internal service. Give it a descriptive
name (e.g.
nextcloud_server), set the IP address to the internal address of the machine running the service (e.g.192.168.1.50), and set the port to the port the service is listening on (e.g.8080). Leave SSL unchecked for now - the proxy handles TLS termination; the backend connection stays on the LAN and can be plain HTTP unless the service requires otherwise. -
Create a Backend Pool for each service. Give it a descriptive
name (e.g.
nextcloud_pool). Under Servers, add the Real Server you just created. Leave the balance algorithm as Round Robin (it doesn't matter with a single server behind each pool).
Repeat this for each service you want to expose. In my setup I have separate pools for several self-hosted services, all running as Docker containers on a home server. Each container gets a fixed internal port; HAProxy routes to the right one based on the hostname.
Step 3: Create the HTTPS frontend
Go to Services → HAProxy → Frontend and create a new frontend. The key settings:
- Name: something like
https_frontend. - Listen Addresses:
0.0.0.0:443to accept connections on all interfaces, or restrict to your WAN IP if preferred. - Type:
SSL / HTTPS (TCP mode). This enables TLS termination at the proxy. - Default Backend Pool: Set this to a backend that returns a sensible error, or leave it empty - any request that doesn't match an ACL rule will fall here. I use a backend pointing to a simple holding page.
- SSL Certificates: For now, add a temporary self-signed certificate issued from OPNsense's own CA (under System → Trust → Certificates). Part 3 replaces this with a proper Let’s Encrypt certificate.
-
SSL Minimum Version: Set to
TLSv1.2. Do not allow TLS 1.0 or 1.1.
Step 4: Add ACL rules and backend switching
Still on the frontend, scroll to the Conditions (ACL) and Actions sections. This is where hostname-based routing is defined.
For each service, add a condition:
- Name: e.g.
is_nextcloud - Test type:
Host matches - Value:
nextcloud.example.com
Then add a corresponding action:
- Action:
Use Backend Pool - Condition:
is_nextcloud - Backend Pool:
nextcloud_pool
Repeat for each hostname and backend pair. HAProxy evaluates conditions in order and routes to the first matching backend.
Step 5: Open the firewall
HAProxy won't receive any traffic until you allow it through the OPNsense firewall. Go to Firewall → Rules → WAN and add a rule:
- Action: Pass
- Protocol: TCP
- Destination: WAN address
- Destination port: 443
If you also want to redirect HTTP to HTTPS (recommended), add a second rule for
port 80, and configure an additional frontend in HAProxy that listens on port 80
and uses an action of HTTP redirect with the scheme set to
https and the code set to 301. This ensures any
plain-HTTP visitors are automatically sent to the secure version.
Step 6: Enable HAProxy and test
Go to Services → HAProxy → Settings and make sure HAProxy is enabled. Click Apply. OPNsense will write the HAProxy configuration and start the service.
To test, try visiting one of your configured hostnames in a browser. You should see a certificate warning (because you're using a self-signed cert) and then the content from your backend service. Accept the warning for now to confirm routing is working. Part 3 will replace the self-signed certificate with a proper Let’s Encrypt certificate so the warning disappears.
If you get a connection refused error, check that the firewall rule is in place and that HAProxy is running. If you get a 502 Bad Gateway, the frontend is working but HAProxy can't reach the backend - check the internal IP and port in your Real Server configuration.
A note on hairpin NAT / NAT reflection
You'll likely want to access your services from inside your own network using the same domain names you use externally. By default, DNS resolves your domain to your public IP, and traffic from inside the LAN to your public IP may not route back in correctly (this is called hairpin NAT or NAT reflection).
There are two approaches: enable NAT reflection on the WAN rules in OPNsense, or create internal DNS overrides in Unbound that point your public domain names to the internal IP of the OPNsense box. The DNS override approach is cleaner. In Unbound (Services → Unbound DNS → Host Overrides), add an override for each hostname pointing to the LAN IP of the OPNsense box. Traffic from inside the LAN will then go directly to OPNsense, which routes it to HAProxy, which routes it to the backend - no hairpin needed.
Self-hosting from home starts with a reliable, fast connection. If you're not yet on YouFibre, referral code KCR5KH is available for new customers. A static IP add-on makes all of this straightforward.
Check Availability at YouFibre