I have some code in my haproxy.cfg file that rate limits requests and returns a 429 and it works beautifully. However, it returns a net::ERR_FAILED and skips past some error handling code I have on the front end. I’ve tried to update the haproxy.cfg to send a status with a content type of “text/plain” so I can handle the status code and let the user know what’s going on, but I’m still getting the net::ERR_FAILED. My update code is as follows:
frontend s4p
bind 192.168.250.146:80
stick-table type binary len 20 size 100k expire 10s store http_req_rate(10s)
# Track client by base32+src (Host header + URL path + src IP)
http-request track-sc0 base32+src
# Check map file to get rate limit for path
http-request set-var(req.rate_limit) path,map_beg(/etc/haproxy/rates.map,20)
# Client's request rate is tracked
http-request set-var(req.request_rate) base32+src,table_http_req_rate()
# Subtract the current request rate from the limit
# If less than zero, set rate_abuse to true
acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0
# Deny if rate abuse
http-request return status 429 content-type "text/plain" if rate_abuse
http-request lua.cors "*" "*" "*"
http-response lua.cors
default_backend api_servers
Can someone help me figure out how to send a response back to the client with a net::ERR_FAILED?
Thanks!
Your browser interprets the 429 response without body and shows you a net::ERR_FAILED
response. Haproxy does not return that.
You say your error handling configuration does not work. Can you show that part of the configuration?
Hi @lukastribus The error handling happens on the front end in my react site, not on the front end of the haproxy config. But here is my entire haproxy config:
# Ansible placed
global
lua-load /etc/haproxy/cors.lua
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-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 ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 60s
timeout server 60s
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
cache mycache
total-max-size 4095 # MB
max-object-size 40000 # bytes
max-age 3600 # seconds
frontend s4p
bind 192.168.250.146:80
stick-table type binary len 20 size 100k expire 10s store http_req_rate(10s)
# Track client by base32+src (Host header + URL path + src IP)
http-request track-sc0 base32+src
# Check map file to get rate limit for path
http-request set-var(req.rate_limit) path,map_beg(/etc/haproxy/rates.map,20)
# Client's request rate is tracked
http-request set-var(req.request_rate) base32+src,table_http_req_rate()
# Subtract the current request rate from the limit
# If less than zero, set rate_abuse to true
acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0
# Deny if rate abuse
http-request return status 429 content-type "text/plain" if rate_abuse
http-request lua.cors "*" "*" "*"
http-response lua.cors
default_backend api_servers
backend api_servers
balance leastconn
default-server check maxconn 20
http-request cache-use mycache
http-response cache-store mycache
server server1 192.168.250.145:80
Haproxy has a simple job and does just that:
It returns a 429 response or it does not, depending on the stick table results. That is it.
If your react site is behind haproxy, the request causing haproxy to return 429 will never even reach react, so whatever error handling you have in react there is irrelevant in this case.
If your react code is somehow between the client and haproxy, I’m not sure what you are actually trying to achieve.
So, my site is outside of haproxy. the client hits my site, which then reaches out to an api server that is behind haproxy. The goal is to stop people from hammering the api server with requests. I just want to get a 429 status code from haproxy when the client has hit a limit and let them know to slow down.
Then your problem is in the react site, as simple as that.
ok, thanks for taking a look at it.