Do you use RemoteIPProxyProtocol? "QS_ClientGeoIpFromHeader X-Forwarded-For" only works if there is a HTTP request header in the request. Alternatively, you could use an Apache module provided by the service you get the geo-data from (I assume this provider offers such a module and that it might relay on mod_remoteip) instead of using mod_qos to set the country code. You can probably still use mod_qos using this country code in your rules.
Hi, after some time I had now the chance to test the new mod_qos version, but it still has problems. Configuration in my config is exactly as mentioned above (Both "QS_Client..Header" directives set). When called from outside via reverse proxy that sets the header, I get: [Thu Apr 23 10:21:42.503300 2026] [qos:error] [pid 3780:tid 2012] [client 77.119.19.3:0] mod_qos(069): no valid IP header found (@prr): header 'X-Forwarded-For' not available, fallback to connection's IP 10.33.200.10 You can see...
Hi, I'm using mod_qos on Apache HTTPd installations providing a whole bunch of vhosts. But on a notable part of it, QoS is not used. For this reason, the QoS status display page is a bit "overloaded" with useless vhost entries stating "uses base server settings and counters" I did a patch introducing an additional "brief" view mode, which will skip the output of this vhosts. Technical, it basically copies an associated logic decision "a level upwards" and skip code using the good-old goto statement....
with the following assumptions: you have a non-Apache reverse proxy in front of your Apache server the proxy forwards the client's IP address (single) in an HTTP request header you have to use mod_remoteip (because of other Apache modules / functionality you used) you want to use mod_qos's geo location feature to set the QS_Country variable you want to use some QS_Client* directives of mod_qos my recommendation is: use mod_qos 11.78 and its new QS_ClientGeoIpFromHeader directive your configuration...
with the following assumptions: you have a non-Apache reverse proxy in front of your Apache server the proxy forwards the client's IP address (single) in an HTTP request header you have to use mod_remoteip (because of other Apache modules / functionality you used) you want to use mod_qos's geo location feature to set the QS_Country variable you want to use some QS_Client* directives of mod_qos my recommendation is: use mod_qos 11.78 and its new QS_ClientGeoIpFromHeader directive your configuration...
with the following assumptions: you have a non-Apache reverse proxy in front of your Apache server the proxy forwards the client's IP address (single) in an HTTP request header you have to use mod_remoteip (because of other Apache modules / functionality you used) you want to use mod_qos's geo location feature to set the QS_Country variable you want to use some QS_Client* directives of mod_qos my recommendation is: use mod_qos 11.78 and its new QS_ClientGeoIpFromHeader directive your configuration...
11.78
QS_ClientGeoIpFromHeader
QS_ClientGeoIpFromHeader
mod_qos was designed to be used in the reverse proxy / at the outermost perimeter where the TCP connection is terminated. You use mod_remoteip for other purpose as well, right? And your proxy sends the IP via HTTP request header (and it is not possible to put it into two headers)? I would rather go with an approach duplicating the header (before mod_remotip removes it) rather than changing the hook sequence for this particular use case. Or I add a directive to define the real header for the use of...
mod_qos was designed to be used in the reverse proxy / at the outermost perimeter where the TCP connection is terminated. You use mod_remoteip for other purpose as well, right? And your proxy sends the IP via HTTP request header (and it is not possible to put it into two headers)? I would rather go with an approach duplicating the header (before mod_remotip removes it) rather than changing the hook sequence for this particular use case. Or I add a directive to define the real header for the use of...
mod_qos was designed to be used in the reverse proxy / at the outermost perimeter where the TCP connection is terminated. You use mod_remoteip for other purpose as well, right? And your proxy sends the IP via HTTP request header (and it is not possible to put it into two headers)? I would rather go with an approach duplicating the header (before mod_remotip removes it) rather than changing the hook sequence for this particular use case. Of add I a directive to define the real header for the use of...
mod_qos was designed to be used in the reverse proxy / at the outermost perimeter where the TCP connection is terminated. You use mod_remoteip for other purpose as well, right? And your proxy sends the IP via HTTP request header (and it is not possible to put it into two headers)? I would rather go with an approach duplicating the header (before mod_remotip removes it) rather than changing the hook sequence for this particular use case. Of add a directive to define the real header for the use of...
mod_qos was designed to be used in the reverse proxy / at the outermost perimeter where the TCP connection is terminated. You use mod_remoteip for other purpose as well, right? And your proxy sends the IP via HTTP request header (and it is not possible to put it into two headers)? I would rather go with an approach duplicating the header (before mod_remotip removes it) rather than changing the hook sequence for this particular use case.
mod_qos was designed to be used in the reverse proxy / at the outermost perimeter where the TCP connection is terminated. You use mod_remoteip for other purpose as well, right? And your proxy sends the IP via HTTP request header (and it is not possible to put it into two headers)? I would rather go with an approach duplicating the header (before mod_remotip removes it) rather than changing the hook sequence for this particular use case. But I need to look at the code again (why is the header already...
Really bad news. And how about adding a dependency in qos_register_hooks (line 14805) as done with other modules to force the correct execution order? Disclaimer: I just did a quick google and looked at the mod_remoteip source. I'm not familiar with the details of the Apache API and far away of being a Apache developer, so I don't want to show off... ;-) The problem is that mod_remoteip removes the header, I cannot use it in mod_qos (although that would mean it is running earlier). And I found no...
revert 11.76
Bad news: mod_remoteip seems to be too late. Workaround: read it from a request header.
I've to confess, that I did not yet write a test case for this change. Could be that mod_remotip sets the value too late and that this was the reason why I did not change the code for this hook.
Thanks a lot. I'll wait for a new apachelounge build and then retest things.
I've uploaded version 11.77 Thank you again for the analysis and the proposed change.
11.77
11.77
11.77
QS_ClientIpFromHeader to support reading the address set by other modules when used in conjunction with the geographical database file
I agree. I will prepare a new version.
Hi, I try to use QS_ClientGeoCountryDB with active mod_remoteip and configured QS_ClientIpFromHeader #USERAGENT_IP Still I get errors in the log: mod_qos(069): no valid IP header found (@prr): header '#USERAGENT_IP' not available, fallback to connection's IP mod_remoteip is active and shows the correct IP in the logs. Looking at the code, I assume it is in mod_qos.c (version 11.76), line 8886 (qos_post_read_request). Here the "old" routine qos_forwardedfor_fromHeader is called, instead the "new"...
But still I didn't really understood why my logs show min 530, 518 ?
But still I didn't really understood why my logs show min 530, 518 ?
You defined a required data rate between 500 (when the server is idle) up to 1500 (when the server reached its maximum number of connections). I would also suggest to enable QS_SrvMinDataRate at a specific number of busy connections to keep it disabled while your server has only a few connections in use, e.g. if you want to support websocket connections.
Say I have QS_SrvMinDataRate 500 1500 set. Now, when i run a slowhttptest I see response as ... mod_qos(034): access denied, QS_SrvMinDataRate rule (in): min=530, this connection=68 ... ... mod_qos(034): access denied, QS_SrvMinDataRate rule (in): min=518, this connection=77 ... why is my min 530, 518 ? also when I try slowhttptest using -x 900 still the QS_SrvMinDataRate is blocking the data. Is there any proper way to test the slowhttptest?
Hi, on 21/10/25 we had the 195.176.113.35 making requests that overcome our threshold configuration for around 6 hours. Almost all requests were towards /app/?login... with 302 response. Checked in our logs an nothing strange or errors are found. Tried to reproduce the issue without luck. Our network has a firewall and a load balancer in front of our 2 proxies. We are using Apache 2.4.59, mod_qos 11.74 and HTTP/2. Could HTTP/2 the cause? Do you have some advice to understand why it happened? This...
I'm sorry for my late response (I somehow forgot about your question). I expect QS_ClientEventLimitCount to work with HTTP/2. I'm not sure about QS_SrvMaxConnPerIP and could imagine that it counts the virtual connections as well. In general about HTTP/2: I try to ensure that the mod_qos base functionality with its request limitations per resource still works but don't intend to extend HTTP/2 support in the future.
Hi Are there plans to extend the functionality of mod_qos so that non-location keywords could be supported with HTTP/2? We are using statements like QS_ClientEventLimitCount or QS_SrvMaxConnPerIP and if I'm not mistaken they are not supported with HTTP/2?
Thanks very much for that suggestion. The QS_VipHeaderName statement was the part I had been missing and I now have it working nicely. The documentation could be a little clearer on this. I had also been confused by the mod-qos-vip reference assuming it referred to an existing internal mod_qos component rather than being an arbitrary user choosable label. Best regards Tom Crane
QS_VipRequest=yes Disables some restrictions for this request (see privileged Users). Requires the definition of a VIP header using the QS_VipHeaderName directive (this activates VIP verification). You also need to enable the privileged users using the QS_VipHeaderName parameter, as the variable set by SetEnvIf is ignored otherwise. As a name for the header you can choose something that does not exist.
Thanks for the suggestion. That is one of the things I had tried previously but w/o success. It seems that whatever I set for the IP regex it does not apply it. It even ignores, SetEnvIf Remote_Addr .* QS_VipRequest=yes I know that it is processing the statement because an illegal regex, e.g. just a * by itself produces an error when starting Apache. I read that mod_qos advises to use MPM Worker. I am using MPM Event but did try with MPM Worker but it made no difference. One possible complicating...
I suggtest to set the QS_VipRequest variable for the requests which you want to exclude from the QS_LocRequestLimitMatch rules. example for illustration: SetEnvIf Remote_Addr 127.0.0.1 QS_VipRequest=yes
I am using QS_LocRequestLimitMatch to restrict the number of concurrent connections to 10 for a particular set of URLs being hammered by bots. This works nicely reducing the server load to something manageable. Now I would like to add some IP block excludes for clients on our local network but cannot get this to work. I have tried using QS_SrvMaxConnExcludeIP which some Google AI search results suggest is indirectly possible. I have also tried marking the IPs as VIPs for use with QS_VipIPHeaderName...
The regular expression QS_LocRequestLimitMatch ^http 50 didn't work, but modifying it to QS_LocRequestLimitMatch ^.*$ 50 solved the problem. Thanks!
QS_LocRequestLimitDefault 50 is basically QS_LocRequestLimit / 50 which does not match your proxy request (which starts with a "h") anymore. I assume that QS_LocRequestLimitMatch ^http 50 will do the trick.
Hello *, I've been struggling with an issue where QS_LocRequestLimitDefault isn't working when combined with mod_rewrite. Here's my configuration: <virtualhost *:80=""> ServerName slow.local Header setIfEmpty Strict-Transport-Security "max-age=31536000; includeSubDomains"</virtualhost> ProxyPreserveHost On RewriteEngine On RequestHeader unset Origin QS_LocRequestLimitDefault 50 RewriteRule ^/(.*)$ http://slow_backend:8000/$1 [L,P] ProxyPassReverse / http://slow_backend:8000/ The interesting part...
've been struggling with an issue where QS_LocRequestLimitDefault isn't working when combined with mod_rewrite. Here's my configuration: <VirtualHost *:80> ServerName slow.local Header setIfEmpty Strict-Transport-Security "max-age=31536000; includeSubDomains" ProxyPreserveHost On RewriteEngine On RequestHeader unset Origin QS_LocRequestLimitDefault 50 RewriteRule ^/(.*)$ http://slow_backend:8000/$1 [L,P] ProxyPassReverse / http://slow_backend:8000/ </VirtualHost> The interesting part is that when...
It appears this issue was fixed in version 11.76.
11.76 cleanup utils
tools cleanup part7
11.76 (util cleanup)
tools cleanup part7
tools cleanup part6
tools cleanup part5
tools cleanup part4
tools cleanup part3
tools cleanup part2
tools cleanup part1
You can log the events and counts as part of you access log entries (adding the to the format definition), e.g. %{SlowRequest}e and %{SlowRequest_Counter}e
Thanks for the reply. My aggressive solution is based on the behavior of some overly-aggressive spiders. I've excluded static resources like images, so the idea is to enforce a reasonable rate on all other requests to my application (30 requests within 2 seconds). I indeed made a typo in my rules: the 20 should be 30 instead. So that explains why I saw log messages related to the "RepeatedlySlow" rule without first seeing "SlowRequest" entries. However, it's still a mystery why that rule was being...
I don't know why you decided on an aggressive two-stage solution. I suggest to start with a simple rule. Know the critical resources (e.g. slow application) which you want to protect and define a rule for that. About the two counters: RepeatedlySlow comes first as it blocks already at 20 requests (though the second time only) while SlowRequest waits until 30.
don't worry, apr_time_t is a 64bit (long or long long depending on your OS) you probably think about the year 2038 bug where 32bit integers will overflow
Yes, the problem is that the calculation for requests per time interval uses timestamps, and they are seconds that have passed from the default year of 1970, this number increases everyday, and now it overflows the datatypes used in the project, specially the apr_time_t. This happens to me with the official docker images.
First, thank you very much for creating this very useful module! My issue is that legitimate spiders like Googlebot are being blocked by mod_qos, even if the number of requests they send is well below the limit I've set. Here are my rules, which are based on your suggestions for some basic DOS protection: # Allows max 100 connections from a single ip address: QS_SrvMaxConnPerIP 100 # Don't allow a client IP to access a "handler" (not a static resource like # a jpg, gif, ..) more than 30 times within...
QS_EventPerSecLimit measures the current "requests per time interval" (default is 5 seconds https://mod-qos.sourceforge.net/#QS_SrvSampleRate) and calculates and applies a delay if necessary, https://mod-qos.sourceforge.net/glossary.html#requestPerSecond You should see "mod_qos(050): request rate limit" messages if a delay gets added. You can also watch the the calculated request rate using the status viewer (mod_status) or measure it by counting the log lines matching your rule (per five second...
I have this simple configuration here:- SetEnvIfExpr "%{HTTP:userId} =~ /([0-9]*)/" USER_ID=$1 CustomLog "logs/custom.log" "USER_ID=%{USER_ID}e" QS_EventPerSecLimit USER_ID 10 And the rate rate limiting is not there at all, I checked the source code and All I could find is that sconf->location_t is getting assigned in the qos_event_rs_cmd function but it never got used for actual rate limiting anywhere. Also many other rate limiting directives there that just modify the sconf variable for the qs_hp_ccfunction...
I have this simple configuration here:- SetEnvIfExpr "%{HTTP:msisdn} =~ /([0-9]*)/" MSISDN=$1 CustomLog "logs/custom.log" "MSISDN=%{MSISDN}e" QS_EventPerSecLimit MSISDN 10 And the rate rate limiting is not there at all, I checked the source code and All I could find is that sconf->location_t is getting assigned in the qos_event_rs_cmd function but it never got used for actual rate limiting anywhere. Also many other rate limiting directives there that just modify the sconf variable for the qs_hp_ccfunction...
I have this simple configuration here:- SetEnvIfExpr "%{HTTP:msisdn} =~ /([0-9]*)/" MSISDN=$1 CustomLog "logs/custom.log" "MSISDN=%{MSISDN}e" QS_EventPerSecLimit MSISDN 10 And the rate rate limiting is not there at all, I checked the source code and All I could find is that sconf->location_t is getting assigned in the qos_event_rs_cmd function but it never got used for actual rate limiting anywhere. Also many other rate limiting directives there that just modify the sconf variable for the qs_hp_ccfunction...
I have this simple configuration here:- SetEnvIfExpr "%{HTTP:msisdn} =~ /([0-9]*)/" MSISDN=$1 CustomLog "logs/custom.log" "MSISDN=%{MSISDN}e" QS_EventPerSecLimit MSISDN 10 And the rate rate limiting is not there at all, I checked the source code and All I could find is that sconf->location_t is getting assigned in the qos_event_rs_cmd function but it never got used for actual rate limiting anywhere. Also other rate limiting directives that just modify the sconf variable for the qoqs_hp_ccfunction...
psre mig util snapshot
Thank you for reporting the issue.
The current latest version of mod_qos, version 11.75, does not build with GCC 14. gcc -DHAVE_CONFIG_H -I. -I.. -DLINUX -D_REENTRANT -D_GNU_SOURCE -Wdate-time -D_FORTIFY_SOURCE=2 -I/usr/include/libpng16 -g -O2 -Werror=implicit-function-declaration -ffile-prefix-map=/<<PKGBUILDDIR>>=. -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection -I/usr/include/apr-1.0 -I/usr/include/apr-1.0 -I/usr/include -I/usr/lib/include -c -o qsfilter2.o qsfilter2.c qsfilter2.c:...
You could use a client event counter to permanently mark an IP address (just ensure you never reach the counter's limit). Example for illustration (I've not tested this myself): # counter to remember VIP QS_ClientEventLimitCount 100 31536000 VIP_IP # keep the counter low (never reach the limit) QS_SetEnv VIP_IP_REFRESH 10 QS_SetEnvIfCmp VIP_IP_Counter gt VIP_IP_REFRESH VIP_IP_Decrement=2 # detect returning VIP user by IP QS_SetEnv VIP_IP_IS_SET 0 QS_SetEnvIfCmp VIP_IP_Counter gt VIP_IP_IS_SET QS_VipRequest=1...
Directives like QS_VipIPUser declare an IP as VIP based on request information. Is there a way to arbitrarily mark IPs as VIP, based on request information, similar to how requests can arbitrarily be marked as VIP by setting an environment variable? For example, if the username matches a regular expression, set environment variable, which will then not only mark the request as VIP but all requests from this IP. The directives QS_VipRequest and QS_IsVipRequest appear to only mark the request itself...
Directives like QS_VipIPUser declare an IP as VIP based on request information. Is there a way to arbitrarily mark IPs as VIP, based on request information, similar to how requests can arbitrarily be marked as VIP by setting an environment variable? For example, if the username matches a regular expression, set environment variable, which will then not only mark the request as VIP but all requests from this IP. The directives QS_VipRequest and QS_IsVipRequest appear to only mark the request itself...
Thanks. I was able to solve it.
I have been trying the exact same thing but its not working for me. Can you please check? Here's my config - # get client IP from HTTP request header instead of client IP QS_ClientIpFromHeader X-Forwarded-For #set this counter on every access ( "/" below is the URL ) SetEnvIf Request_URI / RepeatClientRequest SetEnvIf Remote_Addr 122.160.81.155 !RepeatClientRequest # no more than 2000 requests for any url in 5 minutes QS_ClientEventLimitCount 2000 300 RepeatClientRequest # deny a client IP for 30...
RemoteAddr is the IP address of the client (peer X-Forwarded-For is a HTTP request header field which contains an IP address (often set by proxy servers).
I have been trying the exact same thing but its not working for me. Can you please check? Here's my config - # get client IP from HTTP request header instead of client IP QS_ClientIpFromHeader X-Forwarded-For #set this counter on every access ( "/" below is the URL ) SetEnvIf Request_URI / RepeatClientRequest SetEnvIf Remote_Addr 122.160.81.155 !RepeatClientRequest # no more than 2000 requests for any url in 5 minutes QS_ClientEventLimitCount 2000 300 RepeatClientRequest # deny a client IP for 30...
@pbuchbinder Do we need to do this SetEnvIf X-Forwarded-For xxx.xxx.xxx.xxx !RepeatClientRequest if we are using X-Forwarded-For? QS_ClientIpFromHeader X-Forwarded-F
I solved it using the X-Forwarded-For instead of Remote_Addr The problem was that Remote_Addr did not have the client IP so I had to use the X-Forwarded-For - SetEnvIf X-Forwarded-For xxx.xxx.xxx.xxx !RepeatClientRequest But I wonder how it worked for you when you are also using the X-Forwarded-For QS_ClientIpFromHeader X-Forwarded-F
I solved it using the X-Forwarded-For instead of Remote_Addr The problem was that Remote_Addr did not have the client IP so I had to use the X-Forwarded-For - SetEnvIf X-Forwarded-For xxx.xxx.xxx.xxx !RepeatClientRequest But I wonder how it worked for 'rafa' when he was also using the X-Forwarded-For QS_ClientIpFromHeader X-Forwarded-F
The problem was that Remote_Addr did not have the client IP so I had to use the X-Forwarded-For - SetEnvIf X-Forwarded-For xxx.xxx.xxx.xxx !RepeatClientRequest But I wonder how it worked for @rafa when he was also using the X-Forwarded-For QS_ClientIpFromHeader X-Forwarded-F
Of course, we greatly appreciate the work you do for this project!
setting connection note short-lingering-close when aborting connection
Sali Simon Indeed, MPM event uses a shorter timeout when closing the connection if a module passes this note. I'm going to add this to mod_qos as well. Thank you for sharing this information. Regards, Pascal
Looking through the source code of mod_reqtimeout, I saw that it uses the short-lingering-close request note to considerably shorten the potential duration of a lingering close: https://github.com/apache/httpd/blob/trunk/modules/filters/mod_reqtimeout.c#L329-L335 I did not see this note being used in mod_qos. Would this be useful for mod_qos as well to shorten the lingering close duration in case of QoS intervention?
Looking through the source code of mod_reqtimeout, I saw that it uses the short-lingering-close request note to considerably shorten the potential duration of a lingering close: https://github.com/apache/httpd/blob/trunk/modules/filters/mod_reqtimeout.c#L329-L335 I did not see this note being used in mod_qos. Would this be useful for mod_qos as well to shorten the lingering close duration in case of QoS intervention?
mod_qos version 11.75 is now available. It uses the IP address determined by mod_remoteip by configuring QS_ClientIpFromHeader #USERAGENT_IP
11.75
test env
USERAGENT_IP
USERAGENT_IP
new build srv