-
-
Notifications
You must be signed in to change notification settings - Fork 293
Slowloris SlowHTTP protection
Conor McKnight edited this page Sep 24, 2025
·
5 revisions
The script does a good job at protecting against Slowloris / SlowHTTP based attacks like range attacks and slow requests trying to consume connections and timeout the server.
https://github.com/C0nw0nk/Nginx-Lua-Anti-DDoS/blob/master/lua/anti_ddos_challenge.lua#L171
--[[
Shared memory cache
If you use this make sure you add this to your nginx configuration
http { #inside http block
lua_shared_dict antiddos 70m; #Anti-DDoS shared memory zone to track requests per each unique user
lua_shared_dict antiddos_blocked 70m; #Anti-DDoS shared memory where blocked users are put
lua_shared_dict ddos_counter 10m; #Anti-DDoS shared memory zone to track total number of blocked users
lua_shared_dict jspuzzle_tracker 70m; #Anti-DDoS shared memory zone monitors each unique ip and number of times they stack up failing to solve the puzzle
}
]]
localized.anti_ddos_table = {
{
".*", --regex match any site / path
--limit keep alive connections per ip address until timeout
--the nginx config this is dependant on is keepalive_timeout 75s; https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
0, --unlimited
--status code to exit with when to many requests from same ip are made
--if you are under ddos and want to save bandwidth using localized.ngx_HTTP_CLOSE will save bandwidth.
localized.ngx_HTTP_TOO_MANY_REQUESTS, --429 too many requests around 175 bytes per response
--localized.ngx_HTTP_CLOSE, --444 connection reset 0 bytes per response
--Limit minimum request size to this in bytes requests smaller than this size will be blocked.
40, --0 for no minimum limit size in bytes including request headers
--limit max request size to this in bytes so 1000 bytes is 1kb you can do 1e+9 = 1GB Gigabyte for large sizes
1000000, --0 is unlimited or will fall back to the nginx config value client_max_body_size 1m; https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
--status code to exit with when request size is larger than allowed size
localized.ngx_HTTP_BAD_REQUEST,
--enable or disable logging 1 to enable 0 to disable check your .log file to view logs
1,
--Rate limiting settings
5, --5 second window
60, --max 60 requests in 5s
86400, --86400 seconds = 24 hour block time for ip flooding
localized.ngx_HTTP_CLOSE, --444 connection reset 0 bytes per response
--SlowHTTP / Slowloris settings
128, --Minimum Content-Length in bytes between 0 and this value requests smaller than this will be blocked Expect: 100-continue
10, --Request timeout in seconds requests that take longer than this will be blocked
300, --connections header max timeout value Connection: Timeout=300,Max=1000
100000, --connections header max conns number
localized.ngx_HTTP_CLOSE, --444 connection reset 0 bytes per response
--Range header filter
0, --0 blacklist 1 whitelist
{ --Range header protection SlowHTTP / Slowloris have a range header attack option this is useful to protect against that
--If you set to 0 for blacklist specify each type you want to prevent range headers on like this.
--{"text",}, --block range headers on html/css/js pages
--{"image",}, --block range headers on images
--{"application",}, --block range headers on applications
--{"multipart",}, --block range headers on multipart content
--[[
--You can also allow range headers on all content types and block multi segment ranges like this
{ --0 blacklist for ranges on any type block more than allowed number of segments
"", --empty for any type
10, --Limit occurances block requests with to many 0-5,5-10,10-15,15-30,30-35 multipart/byteranges set to empty string "", to allow any amount
"","",--0,100, --if requesting bytes between 0-100 too small block set to empty string "", to allow any amount
"", --"bytes", --bytes or set to empty string "", to allow any unit type
"", --"[a-zA-Z0-9-%,%=%s+]", --valid chars a-z lowercase A-Z uppercase 0-9 - hyphen , comma = equals and spaces
"",--100, --less than 100 bytes set to empty string "" to skip check
4e+9, --more than 4GB Gigabyte in bytes set to empty string "" to skip check
{ --9th as table to do more advanced range header filtering
{ --1st occurance
"","",--0,100, --between min - max set to empty string "" to skip min - max check
90, --less than 90 bytes set to empty string "" to skip check
"",--20, --more than set to empty string "" to skip check
},
"", --skip 2 set to empty string "" to skip occruance
{ --3rd occurance
"","", --set to empty string "" to skip min - max check
"",--90, --less than 90 bytes set to empty string "" to skip check
20, --more than 20 bytes set to empty string "" to skip check
},
"", --skip 4 set to empty string "" to skip occruance
},
},
]]
--[[
{ --1 whitelist for video type range headers sent for other types not in the whitelist will be blocked
"video", --content type for range request set to empty string "", for any content type
10, --Limit occurances block requests with to many 0-5,5-10,10-15,15-30,30-35 multipart/byteranges set to empty string "", to allow any amount
0,100, --if requesting bytes between 0-100 too small block set to empty string "", to allow any amount --curl -H "Range: bytes=0-5,5-10,10-15,15-30,30-35" http://localhost/video.mp4 --output "C:\Videos" -H "User-Agent: testagent"
"bytes", --bytes or set to empty string "", to allow any unit type
"[a-zA-Z0-9-%,%=%s+]", --valid chars a-z lowercase A-Z uppercase 0-9 - hyphen , comma = equals and spaces
--100, --less than 100 bytes set to empty string "" to skip check
--2e+10, --more than 20GB Gigabyte in bytes set to empty string "" to skip check
},
]]
},
--[[shared memory zones
To use this feature put this in your nginx config
lua_shared_dict antiddos 70m; #Anti-DDoS shared memory zone to track requests per each unique user
lua_shared_dict antiddos_blocked 70m; #Anti-DDoS shared memory where blocked users are put
lua_shared_dict ddos_counter 10m; #Anti-DDoS shared memory zone to track total number of blocked users
lua_shared_dict jspuzzle_tracker 70m; #Anti-DDoS shared memory zone monitors each unique ip and number of times they stack up failing to solve the puzzle
10m can store 160,000 ip addresses so 70m would be able to store around 1,000,000 yes 1 million ips :)
]]
localized.ngx.shared.antiddos, --this zone monitors each unique ip and number of requests they stack up
localized.ngx.shared.antiddos_blocked, --this zone is where ips are put that exceed the max limit
localized.ngx.shared.ddos_counter, --this zone is for the total number of ips in the list that are currently blocked
--Unique identifyer to use IP address works well but set this to Auto if you expect proxy traffic like from cloudflare
--localized.ngx_var_binary_remote_addr, --if you use binary remote addr and the antiddos shared address is 10m in size you can store 160k ip addresses before you need to increase the memory dedicated
"auto", --auto is best but use binary above instead if you want
--Automatic I am Under Attack Mode - authentication puzzle to automatically enable when ddos detected
--1 to enable 0 to disable
1,
--total number of ips active in the block list to trigger I am Under Attack Mode and turn the auth puzzle on automatically
100, --if over 100 ip addresses are currently in the block list for flooding behaviour you are under attack
{ --headers to block i notice slowloris attacks send this header if your under attack and check your logs and see a header or something all attacker addresses have in common this can be useful to block that.
{ --slowhttp / slowloris sends this referer header with all requests
"referer", "http://code.google.com/p/slowhttptest/", --header to match
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
}, --slowloris referer header block
{ --slowhttp / slowloris incase they set it as referrer spelt wrong Intentionally.
"referrer", "http://code.google.com/p/slowhttptest/", --header to match
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
}, --slowloris referrer header block
},
{ --Any $request_method that you want to prohibit use this. Most sites legitimate expected request header is GET and POST thats it. Any other header request types you can block.
--[[
{
"HEAD", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
{
"PATCH", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
{
"PUT", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
{
"DELETE", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
{
"CONNECT", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
{
"OPTIONS", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
{
"TRACE", --https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods#safe_idempotent_and_cacheable_request_methods
localized.ngx_HTTP_CLOSE, --close their connection
1, --1 to add ip to ban list 0 to just send response above close the connection
},
]]
},
1, --0 disable compression 1 enable compression brotli,gzip etc for this domain / path if your under ddos attack the script will turn off gzip since nginx gzip will hog cpu so you dont have to worry about that.
1, --0 disable 1 enable - automatically disable compression for all users if ddos attack detected if more than number of IPs end up in the ban list the server will prevent cpu intensive tasks like compression to stay online.
--Javascript puzzle flood protection
--In the event of an attack a user who fails to solve the javascript puzzle after a certain number of times will have their ip blocked
--lua_shared_dict jspuzzle_tracker 70m; #Anti-DDoS shared memory zone monitors each unique ip and number of times they stack up failing to solve the puzzle
localized.ngx.shared.jspuzzle_tracker, --this zone monitors each unique ip and number of times they stack up failing to solve the puzzle
35, --35 second window
4, --max 4 failures in 35s
--When a IP is in the blocklist for flooding or attacking to prevent their request even reaching the nginx process you can use this to execute custom scripts or commands on your server to block the ip before it even reaches the nginx process
--You can do this with linux so anyone who gets blocked will be blocked at the server / router level before they reach your nginx process.
--Linux examples
--"iptables -A INPUT -s "..localized.ngx_var_remote_addr.." -j DROP",
--"./do_something.sh "..localized.ngx_var_remote_addr.."",
--Windows examples
--"notepad.exe"
--"netsh advfirewall firewall add rule name=\"Block "..localized.ngx_var_remote_addr.."\" protocol=any dir=out remoteip="..localized.ngx_var_remote_addr.." action=block"
--Default is nil or "" to not do anything
nil,
},
}
https://github.com/C0nw0nk/Nginx-Lua-Anti-DDoS/blob/master/lua/anti_ddos_challenge.lua#L206
If you try to attack via curl -H "Range: bytes=0-5,5-10,10-15,15-30,30-35" http://localhost/video.mp4 --output "C:\Videos" -H "User-Agent: testagent"
Launching loads of small byte ranges this script will let you block those.
10, --Limit occurances block requests with to many 0-5,5-10,10-15,15-30,30-35 multipart/byteranges set to empty string "", to allow any amount