HAProxy in pfSense - Reverse Proxy and Load Balancing
HAProxy (High Availability Proxy) is a high-performance solution for proxying and load balancing TCP and HTTP traffic. In pfSense, HAProxy is installed as a package and provides reverse proxy functionality, load balancing across multiple servers, SSL termination to offload encryption from backend servers, and HTTP request routing based on ACL rules. HAProxy is widely used for publishing web applications through a single external IP address, ensuring server pool failover, and distributing client requests across multiple application instances.
Within the pfSense context, HAProxy operates alongside the firewall, enabling routing, filtering, and proxying functions on a single device. This simplifies network architecture and reduces points of failure.
Installing HAProxy
Installation is performed through the package manager:
- Navigate to System > Package Manager > Available Packages
- Search for haproxy (two versions are available: haproxy and haproxy-devel)
- Click Install and confirm the installation
- Wait for the installation to complete
After installation, configuration is accessible at Services > HAProxy.
Version Selection
| Version | Description | Recommendation |
|---|---|---|
| haproxy | Stable version, tracks the stable FreeBSD port branch | Production environments |
| haproxy-devel | Development version, receives new features first | Test environments |
HAProxy Architecture
HAProxy operates with two primary entities:
Client --> Frontend (listener) --> ACL (routing) --> Backend (server pool)
|-- Server 1
|-- Server 2
|-- Server 3| Component | Description |
|---|---|
| Frontend | Entry point for client connections (IP:port), defines protocol and ACL rules |
| Backend | Pool of backend servers with load balancing and health check settings |
| Server | Individual backend server in the pool (IP, port, weight) |
| ACL | Condition for routing requests between backends |
pfSense Implementation Details
The pfSense web interface differs from standard HAProxy configuration. Instead of separate frontend/backend sections in the configuration file, the pfSense package generates listen sections that combine frontend and backend. This simplifies basic setup but limits capabilities for advanced multi-backend scenarios.
Backend Configuration
A backend defines the group of servers to which HAProxy directs client requests. Configuration is performed through Services > HAProxy > Backend.
Creating a Backend
- Navigate to Services > HAProxy > Backend
- Click Add
- Complete the parameters
- Click Save and Apply Changes
Core Backend Parameters
| Parameter | Description |
|---|---|
| Name | Unique backend name (no spaces) |
| Server List | Pool server list |
| Balance | Load balancing algorithm |
| Connection Timeout | Server connection timeout |
| Server Timeout | Server response timeout |
| Retries | Number of connection retry attempts |
Backend Servers
For each server in the pool, specify:
| Field | Description | Required |
|---|---|---|
| Name | Server name for log identification | Yes |
| Address | Backend server IP address or FQDN | Yes |
| Port | Backend server port | Yes |
| SSL | Use SSL when connecting to the server | No |
| Weight | Server weight for weighted balancing (1-256) | No |
| Cookie | Cookie value for session persistence | No |
Load Balancing Algorithms
| Algorithm | Description | Use Case |
|---|---|---|
| Round Robin | Sequential distribution across servers | Servers with equal performance |
| Least Connections | Routes to the server with fewest connections | Requests with varying processing times |
| Source | Client-to-server binding based on IP address | Session persistence without cookies required |
| URI | Routing by request URI | Caching servers |
| First | Fills servers sequentially to capacity | Minimizing active server count |
For most web applications, Round Robin or Least Connections is recommended. The Source algorithm suits applications that do not support distributed sessions.
Frontend Configuration
A frontend defines the entry point for client connections - the IP address, port, and protocol on which HAProxy accepts connections. Configuration is performed through Services > HAProxy > Frontend.
Creating a Frontend
- Navigate to Services > HAProxy > Frontend
- Click Add
- Complete the parameters
- Click Save and Apply Changes
Core Frontend Parameters
| Parameter | Description |
|---|---|
| Name | Unique frontend name |
| Status | State: Active or Disabled |
| External Address | Listening IP address (WAN, VIP, localhost) |
| External Port | Listening port |
| Max Connections | Maximum simultaneous connections |
| Type | Type: HTTP/HTTPS (Layer 7) or TCP (Layer 4) |
| Default Backend | Default backend for requests without ACL matches |
HTTPS Frontend Configuration
To accept HTTPS connections:
- Set Type to HTTP / HTTPS (offloading)
- In the SSL Offloading section, select the certificate
- Set External Port to 443
- Configure the Default Backend
To redirect HTTP to HTTPS, create an additional Frontend on port 80 with an http-request redirect action or use an ACL.
Binding to Virtual IP
To accept connections on a specific IP address different from the primary WAN address, use a Virtual IP:
- Create an IP Alias or CARP VIP through Firewall > Virtual IPs
- In the Frontend settings, select the created VIP in the External Address field
This enables serving multiple domains on different IP addresses through a single HAProxy instance.
ACL - Routing Rules
ACLs (Access Control Lists) define conditions by which HAProxy routes requests to different backends. ACLs analyze incoming request parameters and apply the corresponding action.
Creating ACLs
ACLs are configured within the Frontend section:
- On the Frontend editing page, navigate to the Access Control Lists section
- Click Add ACL (down arrow)
- Complete the condition and action
- Repeat for each routing rule
ACL Condition Types
| Condition | Description | Example |
|---|---|---|
| Host matches | Host header match | app.example.com |
| Host starts with | Host header prefix | api. |
| Host ends with | Host header suffix | .example.com |
| Host contains | Substring in Host header | staging |
| Host regex | Regular expression for Host | ^(www\.)?example\.com$ |
| Path starts with | URI path prefix | /api/ |
| Path ends with | URI path suffix | .php |
| Path contains | Substring in URI path | /admin/ |
| Path regex | Regular expression for path | ^/v[0-9]+/ |
| Source IP matches | Client IP address match | 192.168.1.0/24 |
| SSL SNI matches | SNI match in TLS Client Hello | app.example.com |
| Custom ACL | Custom HAProxy condition | hdr(X-Custom) -i value |
ACL Actions
| Action | Description |
|---|---|
| Use Backend | Route request to specified backend |
| http-request deny | Reject request with error code |
| http-request redirect | Redirect request to another URL |
| http-request set-header | Set or modify HTTP header |
Domain-Based Routing Example
Publishing multiple web applications through a single external IP address:
| ACL | Condition | Backend |
|---|---|---|
| ACL 1 | Host matches app1.example.com | backend_app1 |
| ACL 2 | Host matches app2.example.com | backend_app2 |
| ACL 3 | Host matches api.example.com | backend_api |
| Default | All other requests | backend_default |
Path-Based Routing Example
| ACL | Condition | Backend |
|---|---|---|
| ACL 1 | Path starts with /api/ | backend_api |
| ACL 2 | Path starts with /static/ | backend_static |
| Default | All other requests | backend_webapp |
SSL Termination
SSL termination (SSL offloading) allows HAProxy to accept HTTPS connections from clients, decrypt the traffic, and forward it to backend servers over HTTP. This removes the encryption burden from backend servers and simplifies certificate management.
Importing a Certificate
Before configuring SSL, import the certificate into pfSense:
- Navigate to System > Certificates > Certificates
- Click Add/Sign
- Select method: Import an existing Certificate
- Paste the certificate and private key
- Click Save
For Let’s Encrypt certificates, the ACME package is recommended for automatic issuance and renewal.
Configuring SSL Offloading in Frontend
- In the Frontend settings, set Type to HTTP / HTTPS (offloading)
- In the SSL Offloading section, select the certificate
- Optionally add extra certificates through Additional Certificates (for multiple domains)
- Configure the minimum TLS version (TLS 1.2 recommended)
SSL Parameters
| Parameter | Description | Recommendation |
|---|---|---|
| Certificate | Primary SSL certificate | Required |
| Additional Certificates | Certificates for additional domains | As needed |
| SSL/TLS Minimum Version | Minimum protocol version | TLS 1.2 |
| SSL/TLS Ciphers | Cipher suite | Default or Mozilla Modern |
| HSTS | HTTP Strict Transport Security | Enable for production |
| OCSP Stapling | OCSP status stapling | Enable for improved performance |
SSL Pass-Through
When SSL termination must occur on the backend server (e.g., client certificates), use the SSL/HTTPS (TCP mode) type. HAProxy passes encrypted traffic without decryption, routing by SNI.
Health Checks
Health checks enable HAProxy to determine backend server availability and automatically remove unresponsive servers from the balancing pool.
Check Types
| Type | Description | Parameters |
|---|---|---|
| TCP Check | TCP connection check to port | No configuration required |
| HTTP Check | Sends HTTP request and verifies response code | URI, method, expected code |
| SSL Check | SSL connection check | Similar to TCP with SSL |
| LDAP Check | LDAP server verification | LDAP-specific parameters |
| MySQL Check | MySQL server verification | Credentials |
| PostgreSQL Check | PostgreSQL server verification | Credentials |
| SMTP Check | SMTP server verification | HELO domain |
| ESMTP Check | Extended SMTP verification | EHLO domain |
Configuring HTTP Checks
For web applications, HTTP checks are recommended:
- In the Backend settings, navigate to Health Checking
- Set Health Check Method to HTTP
- Specify Check Frequency (check interval, e.g., 5 seconds)
- Specify Health Check URI (check path, e.g.,
/health) - Specify HTTP Check Method (GET)
- Specify the expected response code (default 200-399)
Check Parameters
| Parameter | Description | Default |
|---|---|---|
| Check Frequency | Interval between checks | 5 seconds |
| Inter | Check interval for healthy servers | 5000 ms |
| Down Inter | Check interval for unavailable servers | 1000 ms |
| Rise | Successful checks required to restore server | 2 |
| Fall | Failed checks required to remove server | 3 |
Session Persistence
Session persistence (sticky sessions) ensures all requests from a single client are directed to the same backend server. This is necessary for applications that store session state locally on the server.
Persistence Methods
| Method | Description | Use Case |
|---|---|---|
| Cookie-based | HAProxy inserts a cookie with server identifier | Web applications with cookie support |
| Source IP | Binding by client IP address (Source algorithm) | Applications without cookies, APIs |
| SSL Session ID | Binding by TLS session identifier | HTTPS without cookies |
Configuring Cookie-Based Persistence
- In the Backend settings under Cookie Persistence:
- Cookie Name - cookie name (e.g.,
SERVERID) - Cookie Mode - mode: Insert (HAProxy inserts cookie), Prefix, Rewrite
- Cookie Options - additional parameters (Indirect, NoCache, PostOnly)
- Cookie Name - cookie name (e.g.,
- For each server in the backend, specify a unique value in the Cookie field
Statistics Page
HAProxy provides a built-in statistics page with information about the state of frontends, backends, and individual servers.
Enabling Statistics
Configuration is performed through Services > HAProxy > Settings under the Stats section:
| Parameter | Description |
|---|---|
| Stats Enabled | Activate the statistics page |
| Stats URI | Access URI (e.g., /haproxy-stats) |
| Stats Realm | Authentication dialog title |
| Stats Username | Username |
| Stats Password | Password |
| Stats Admin | Allow server management through the web interface |
| Stats Node | Node name displayed in statistics |
Statistics Page Information
The statistics page displays:
- State of each frontend (UP/DOWN, current connections)
- State of each backend and its servers (UP/DOWN, active/inactive)
- Request counts, bytes, and errors for current and previous sessions
- Server response times (avg, max)
- Queue status (request queue during server overload)
- Server weights and distribution ratios
With Stats Admin enabled, the following actions are available: disabling/enabling servers, draining connections, setting weights.
Warning:
The statistics page contains sensitive infrastructure information. Always configure authentication and restrict access by IP address through firewall rules or ACLs.
Common Use Cases
Reverse Proxy for Web Servers
Publishing multiple web applications through a single external IP address with domain-based routing:
- Create a Backend for each web application (backend_site1, backend_site2)
- Create a Frontend on port 443 with SSL termination
- Configure ACL routing rules by Host
- Create a Frontend on port 80 with HTTPS redirect
- Configure firewall rules to allow traffic on ports 80 and 443
Web Application Load Balancing
Distributing requests across multiple application instances:
- Create a Backend with multiple servers
- Select the balancing algorithm (Round Robin or Least Connections)
- Configure HTTP health check on the
/healthendpoint - Configure Cookie-based session persistence if required
- Create a Frontend bound to a VIP or WAN address
API Publishing
Routing API requests with version separation:
- Create a Backend for each API version
- Create a Frontend with path-based ACLs:
/v1/- backend_v1,/v2/- backend_v2 - Configure health checks for each backend
- Add rate limiting through ACLs if needed
Global Settings
General HAProxy parameters are configured through Services > HAProxy > Settings.
| Parameter | Description | Recommendation |
|---|---|---|
| Enable HAProxy | Activate HAProxy | Enable |
| Maximum Connections | Global connection limit | 1000-10000 (depends on RAM) |
| Internal Connections Timeout | Client connection timeout | 30000 ms |
| Connection Timeout | Backend connection timeout | 30000 ms |
| Server Timeout | Backend response timeout | 30000 ms |
| Tunnel Timeout | Timeout for WebSocket and long-lived connections | 3600000 ms |
| DNS Resolvers | DNS servers for backend name resolution | Specify when using FQDN |
Logging
HAProxy writes events to the pfSense system log (Status > System Logs > HAProxy).
Log Levels
| Level | Description |
|---|---|
| Emergency | System is unusable |
| Alert | Immediate action required |
| Critical | Critical errors |
| Error | Connection and processing errors |
| Warning | Warnings (server unavailability) |
| Notice | Normal but significant events |
| Info | Informational messages about requests |
| Debug | Detailed debug information |
HTTP Log Format
Each HTTP request log line contains:
- Client IP and port
- Connection acceptance time
- Frontend and backend names
- HTTP response code
- Processing duration (Tq/Tw/Tc/Tr/Tt)
- Transferred data volume
- Request headers (when capture is enabled)
Troubleshooting
HAProxy Fails to Start
- Check configuration: Services > HAProxy > Settings > Configuration Validity
- Review system logs: Status > System Logs > HAProxy
- Verify frontend ports are not occupied by other services
- Confirm SSL certificates are valid and not expired
Backend Server Marked as DOWN
- Verify server reachability from pfSense: Diagnostics > Ping
- Confirm the backend server port accepts connections: Diagnostics > Test Port
- Review health check settings - URI, method, expected response code
- Examine HAProxy logs to determine the failure cause
- Temporarily disable the health check for diagnostics
502 Bad Gateway
- The backend server is not responding or returning an error
- Check the backend connection timeout (Connection Timeout)
- Verify the backend server can handle the request (not overloaded)
- When using SSL to the backend, verify certificate correctness
503 Service Unavailable
- All servers in the backend are marked as DOWN
- Review health checks for each server
- Verify the maximum connection count is not exceeded
- Check the request queue on the statistics page
SSL Errors
- Check certificate expiration: System > Certificates
- Verify the certificate chain is complete (including intermediate CAs)
- Confirm the domain name in the certificate matches the requested domain
- When using multiple certificates, verify SNI is configured correctly
- Check the minimum TLS version - older clients may not support TLS 1.2+
Session Not Persisting Between Requests
- Review Cookie Persistence settings in the backend
- Verify each server has a unique Cookie value
- When using the Source algorithm, confirm clients connect from a consistent IP
- Check whether a transparent proxy or CDN is modifying the client IP
Related Sections
- Package Management - installing and updating pfSense packages
- pfSense Certificates - managing SSL certificates for HAProxy
- Firewall Rules - configuring rules to allow traffic to HAProxy
- pfSense NAT - alternative method for publishing services through port forwarding