@@ -26,14 +26,14 @@ defaults
2626 timeout http-keep-alive 60s
2727 timeout queue 30s
2828
29- frontend https_in
30- bind *:443 ssl crt /etc/haproxy/certs/origin.pem alpn h2,http/1.1 strict-sni
29+ frontend http_in
30+ bind *:80
3131
3232 # Gzip compression
3333 compression algo gzip
3434 compression type text/html text/plain application/json text/css
3535
36- # Access to port 443 is already restricted to Cloudflare IPs at the firewall level
36+ # Access to port 80 is restricted to Cloudflare IPs at the firewall level
3737 # All requests reaching HAProxy come from Cloudflare; no additional ACL validation is required.
3838
3939 # HAProxy's own forwardfor is not enabled; real IP is set explicitly below via CF-Connecting-IP
@@ -62,6 +62,40 @@ frontend https_in
6262
6363 default_backend app
6464
65+ frontend https_in
66+ bind *:443 ssl crt /etc/haproxy/certs/origin.pem alpn h2,http/1.1 strict-sni
67+
68+ # Gzip compression
69+ compression algo gzip
70+ compression type text/html text/plain application/json text/css
71+
72+ # Access to port 443 is restricted to Cloudflare IPs at the firewall level
73+ # All requests reaching HAProxy come from Cloudflare; no additional ACL validation is required.
74+
75+ # HAProxy's own forwardfor is not enabled; real IP is set explicitly below via CF-Connecting-IP
76+
77+ # Overwrite standard headers directly with the real IP forcibly added by Cloudflare
78+ http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)]
79+ http-request set-header X-Real-IP %[req.hdr(CF-Connecting-IP)]
80+
81+ # Rate limiting uses the shared stick-table from http_in frontend
82+ http-request track-sc0 req.hdr(CF-Connecting-IP) table http_in
83+ # Track global request rate (per second)
84+ http-request track-sc1 str(global) table global_rate_tracking
85+
86+ # Tier 3: global >= 200 RPS -> limit each IP to 5 RPM
87+ http-request deny deny_status 429 if { sc_http_req_rate(1,global_rate_tracking) ge 200 } { sc_http_req_rate(0,http_in) gt 5 }
88+ # Tier 2: global >= 100 RPS -> limit each IP to 10 RPM
89+ http-request deny deny_status 429 if { sc_http_req_rate(1,global_rate_tracking) ge 100 } { sc_http_req_rate(0,http_in) gt 10 }
90+ # Tier 1: default -> limit each IP to 30 RPM
91+ http-request deny deny_status 429 if { sc_http_req_rate(0,http_in) gt 30 }
92+
93+ # Block /health from external access
94+ acl is_health path /health
95+ http-request deny deny_status 403 if is_health
96+
97+ default_backend app
98+
6599backend global_rate_tracking
66100 stick-table type string len 6 size 1 expire 10m store http_req_rate(1s)
67101
0 commit comments