I made some changes to my config as per this suggestion:

SecAction \

But nothing seems to happen. I noticed that my apache error_log was using the default error log format and logging everything as

So I changed my ErrorLogFormat to:

ErrorLogFormat "[%{u}t] [%-m:%l] [pid %P:tid %T] %7F: %E: [client\ %{X-Forwarded-For}i] %M% ,\ referer\ %{Referer}i"

That made my logs better, but ModSecurity is still not doing any blocking. What's weird is that most of the ModSec logs in the apache error_log have an extra client IP section in them:

[Wed May..2019] [err] [pid X:tid X] [client XXX.XX.XX.XXX] [client] ModSecurity: Warning...

I have no idea where the extra [client] is coming from - I know it's definitely only doing this for ModSecurity logs in the error_log.

It seems like either ModSecurity is either constantly trying to block or just not blocking anything..

So how can I get ModSecurity to block using the X-Forwarded-For header?


  1. I do have SecRuleEngine On set properly.
  2. Versions: Apache 2.4, ModSecurity 2.9, OWASP 3

The first part of your linked suggestion has this

SecAction "phase:1,nolog,pass,initcol:IP=%{REQUEST_HEADERS.x-forwarded-for}"

As you can see it specifies that x-forwarded-for is part of REQUEST_HEADERS but that is missing from your version of this.

Do be aware that just logging the IP will not cause blocking. It is used to persist data that can be used in subsequent rules (e.g. log a counter against an IP and iterate it with each request, and then block if it goes above a certain limit for basic DoS protection). So make sure you’ve got some rules configured to DO something with that IP address!

Also, as discussed in the comments, ModSecurity logs collections in a disk based file. This causes contention when lots of Apache processes try to access it at the same time. And the cleanup that ModSecurity does also can fail meaning the file grows and grows until it eats up all disk space or slows Apache to a crawl. I am not a fan of using them, especially on a site with an volume. I see them as a proof of concept of what ModSecurity could do to expand its single request rules based engine to a cross request one, but it’s not production ready IMHO. For more on this see here: https://sourceforge.net/p/mod-security/mailman/message/34393121/

The double client_ip logging is a remnant of when ModSecurity used to use a non-standard way of logging. They moved to Apache native logging (which has more features - like the ability to log HTTP Headers like x-forwarded-for) but only noticed late the double client_ip (as both Apache standing logging module and ModSecurity is now logging this). They left as is to avoid breaking anyone who depended on the original client_ip logging. See here for more details: https://github.com/SpiderLabs/ModSecurity/pull/840

  • Thanks and good answer, (+1 for the quality info); but after making this change I still see [Timestamp] UNIQUE_ID_HASH XXXXX XX in the audit log. – Yes Barry May 24 at 16:06
  • 1
    No that’s not how it works. IP address is used for specific rules. Look at DOS rules or Brute Force rules which are in the experimental folder if on v2 of the CRS. They are not used to block all messages after a single failed rule. You could write such a rule based on DOS rule but I could imagine that would be quite dangerous as prone to false positives. – Barry Pollard May 24 at 16:28
  • 1
    In general ModSecurity blocks requests and not users. So it interrupts Apache processing if a rules is fired and instead of returning the result it returns a 403. Other requests from that IP still work (assuming they don’t fire rules) as they don’t check IP collections. If you have the DoS rules enabled then they do check the IP collections each time and they pass certain thresholds then it blocks ALL requests from that IP for a period of time. However the DoS limits are typically not set to 1 bad request as it seems you want - though could configure like this I suppose. If collections worked! – Barry Pollard May 24 at 16:42
  • 1
    And yes anomaly scoring requires blocking rules to be above an anomaly score before it blocks. This may be just triggered by one bad rule failure (as I would expect with an obvious SQLi) or it might be lots of little rules fails. Counting separate rules for one request doesn’t require shared storage as all rules for one request are handled by one process so can share in memory. Error log should show anomaly score for each rule that is fired. Which is one reason I dislike anomaly scoring - it’s very noisy! serverfault.com/questions/888168/… – Barry Pollard May 24 at 17:00
  • 1
    Ah so you are on CRSv3. Yeah Paranoia level 1 doesn’t always block when it should. But then also doesn’t usually accidentally block when it shouldn’t. WAFs are very prone to false positives and blocking legitimate requests when they shouldn’t. So need to be tuned to avoid this. As you move up the Paranoia levels you need more tuning based on your site and the requests you get. Paranoia level 1 usually needs no tuning but, as I say, can allow requests that should be blocked. – Barry Pollard May 24 at 17:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.