This episode bundles all the security lessons into one package: best practices for ACLs, timeouts, and connection limits, preventing protocol downgrades and header injections, and safe default configuration for production operations.

A loose configuration is an accidental open door. Hardening isn't about adding exotic features; it's about making every default decision conservative and closing every unnecessary surface.
Episode 14 unifies the security practices from across the series into one reference configuration: sensible ACL and timeout policies, prevention of downgrades and injections, and a file structure that's safe to operate.
Timeouts that are too long let empty connections waste resources; ones that are too short cut off legitimate users. Commonly used values:
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
timeout queue 10s
timeout tunnel 1hThe timeout connect 5s directive rejects backends that don't respond within 5 seconds. timeout http-request 10s prevents clients from holding half-sent requests, and timeout tunnel 1h leaves room for long connections like WebSocket.
Strict connection policies protect against running out of resources:
global
maxconn 20000
defaults
maxconn 5000
timeout queue 10s
backend api_back
server api1 10.0.0.11:8080 maxconn 1500
server api2 10.0.0.12:8080 maxconn 1500The maxconn 20000 directive in global is the process's hard cap; the per-backend and per-server limits below it provide layered defense. timeout queue makes sure requests only wait in the queue briefly.
A set of security ACLs worth having on almost every frontend:
frontend web_front
bind *:80
mode http
acl has_host hdr(host) -m found
acl is_https ssl_fc
acl bad_method method TRACE CONNECT
acl suspicious_path path_reg -i /(\.env|\.git|\.bak)
http-request deny deny_status 400 if !has_host
http-request deny deny_status 403 if bad_method
http-request deny deny_status 403 if suspicious_path
default_backend web_backacl has_host hdr(host) -m found rejects requests without a Host header (a sign of automated probes), and suspicious_path blocks access to leaked configuration files.
A protocol downgrade happens when a client is forced to use a weak TLS version. Lock them all from the server side:
global
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
ssl-default-bind-ciphersuites \
TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
ssl-default-server-options no-sslv3ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 disables protocols that are no longer safe. When the backend also uses TLS, ssl-default-server-options no-sslv3 prevents HAProxy as a client from offering legacy protocols.
Unvalidated headers can be used to smuggle in newlines or suspicious content. Sanitize headers coming from clients:
frontend web_front
bind *:80
mode http
http-request del-header Proxy-Connection
http-request del-header X-Forwarded-For
http-request set-header X-Forwarded-Proto http
default_backend web_backhttp-request del-header X-Forwarded-For removes headers spoofed by clients before the backend reads the value HAProxy itself generated. The principle: never trust a header from the client without overwriting it.
Some paths must never be reachable from outside:
frontend web_front
bind *:80
mode http
acl is_local src 127.0.0.1 10.0.0.0/8
acl is_admin_path path_beg /admin /api/v1/internal
http-request deny deny_status 404 if is_admin_path !is_local
default_backend web_backacl is_local src 127.0.0.1 10.0.0.0/8 defines the internal network, and http-request deny deny_status 404 if is_admin_path !is_local makes admin endpoints invisible from outside (404, not 403, so they don't attract attention).
Version information helps attackers hunt for vulnerabilities. Hide identifying marks:
frontend web_front
bind *:80
mode http
http-response set-header Server "haproxy"
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-Frame-Options DENY
default_backend web_backhttp-response set-header X-Content-Type-Options nosniff prevents browsers from guessing content types, and X-Frame-Options DENY refuses page loading in other sites' iframes — basic defense against clickjacking.
Security is also about file access permissions:
sudo chown root:root /etc/haproxy/haproxy.cfg
sudo chmod 600 /etc/haproxy/haproxy.cfg
sudo chmod 660 /run/haproxy.socksudo chmod 600 /etc/haproxy/haproxy.cfg makes sure only root can read a configuration that may contain secrets. The 660 socket permission limits who can use the runtime API.
A configuration error in production is as dangerous as an attack:
haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxyhaproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy only reloads if the syntax is valid. Make this a habit in every environment, including CI/CD (episode 20).
Episode 14 turns habits into policy: sensible timeouts, strict limits, locked-down protocols, sanitized headers, and file permissions that are kept in check. A secure configuration isn't a feature — it's the default.
Key takeaways:
maxconn protect against running out of resources.haproxy -c validation is the gate before every reload.In the next episode we'll cover performance tuning & scalability — tuning maxconn, buffers, and threads, reducing latency with keepalive, compression, and HTTP/2, and benchmarking with wrk, hey, and h2load.