Files
rustyip/haproxy/haproxy.cfg
T
NetworkCatsandClaude Opus 5.5 c05b71e33b Raise rate limits
Each IP may now make 120 requests per minute instead of 60. Under
load the limit drops to 60 RPM once the site reaches 600 RPS and to
30 RPM at 1000 RPS, instead of 30 and 15 RPM at 200 and 300 RPS.

One page load makes three or four requests, and many visitors share
an IP behind carrier-grade NAT or an office gateway, so the old
limits throttled real users. The old global thresholds were also far
below what the host can serve. Update the FAQ in every locale to
match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:33:39 +08:00

68 lines
3.0 KiB
INI

global
log stderr local0 err
maxconn 4096
# TLS configuration optimized for Cloudflare-to-origin connections
ssl-default-bind-curves X25519:prime256v1:secp384r1
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlog-normal
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
timeout http-keep-alive 5s
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/origin.pem crt /etc/haproxy/certs-ipv4/origin.pem alpn h2,http/1.1 strict-sni
# Gzip compression
compression algo gzip
compression type text/html text/plain application/json text/css application/javascript
# Access to port 443 is already restricted to Cloudflare IPs at the UFW level
# Therefore, all requests reaching HAProxy come from Cloudflare; no additional ACL validation is required.
# Identify IPv4-only domain traffic by Host header
acl is_ipv4_domain hdr(host) -f /usr/local/etc/haproxy/ipv4_domain.lst
# HAProxy's own forwardfor is not enabled; real IP is set explicitly below via CF-Connecting-IP
# Main domain: standard headers
http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)] if !is_ipv4_domain
http-request set-header X-Real-IP %[req.hdr(CF-Connecting-IP)] if !is_ipv4_domain
# IPv4-only domain: separate header to prevent confusion with the main domain
http-request set-header X-IPv4-Client-IP %[req.hdr(CF-Connecting-IP)] if is_ipv4_domain
http-request del-header X-Forwarded-For if is_ipv4_domain
http-request del-header X-Real-IP if is_ipv4_domain
# Rate limiting: tiered based on global request rate
# Track per-IP request rate (per minute)
stick-table type string len 45 size 100k expire 1m store http_req_rate(1m)
http-request track-sc0 req.hdr(CF-Connecting-IP)
# Track global request rate (per second)
http-request track-sc1 str(global) table global_rate_tracking
# Tier 3: global >= 1000 RPS -> limit each IP to 30 RPM
http-request deny deny_status 429 if { sc_http_req_rate(1,global_rate_tracking) ge 1000 } { sc_http_req_rate(0) gt 30 }
# Tier 2: global >= 600 RPS -> limit each IP to 60 RPM
http-request deny deny_status 429 if { sc_http_req_rate(1,global_rate_tracking) ge 600 } { sc_http_req_rate(0) gt 60 }
# Tier 1: default -> limit each IP to 120 RPM
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 120 }
# Block /health from external access
acl is_health path /health
http-request deny deny_status 403 if is_health
default_backend app
backend global_rate_tracking
stick-table type string len 6 size 1 expire 10m store http_req_rate(1s)
backend app
server rustyip app:3000 check