The Noiz ModSecurity Rule Set (Custom WAF Rules)
This guide explains the custom ModSecurity rule set that Noiz runs on its web servers: what each rule does in plain language, the policy behind it, and how the rule set is verified and kept current. It is the canonical reference for the rules themselves and is updated as the deployed rules change (currently v8, August 2026). It is written for Noiz operations.
For how to deploy the rules to a specific panel, see the panel guide: Deploy the Noiz ModSecurity Rules on Plesk or Deploy the Noiz ModSecurity Rules on ISPConfig. For diagnosing blocks, see Troubleshooting the Noiz ModSecurity Custom Rules.
Last reviewed: 1 August 2026, against the deployed Noiz rule set v8 with ModSecurity 2. It complements the official ModSecurity documentation; the rule IDs and thresholds are the Noiz-specific ones.
What this rule set protects
Section titled “What this rule set protects”The Noiz estate hosts WooCommerce shops. The rules are built around one observed reality: when a shop is attacked, the attack is a flood of shopping-request shapes — facet filtering, add-to-cart, cart/checkout URLs — replayed from many IPs to burn CPU. The rule set has three jobs:
- Flood defence — rate-limit and reject the shopping-flood shapes (rules 1000200–1000203, 1000210).
- Bot policy — allow verified search engines, rate-limit AI crawlers, block known-bad named crawlers, and let everything else fall under the per-IP rate rules (rules 1000199, 1000196, 88345501–88345506).
- Hygiene — never let client-supplied credentials land in logs (88349100).
The policy behind the bot rules is four tiers:
| Tier | Who | Treatment |
|---|---|---|
| T1 | Verified search engines (Googlebot, Bingbot, Applebot, DuckDuckBot, Yandex, Baidu, Facebook link-preview) | Allow, exempt from all flood rules (IP allowlist, DNS-verified) |
| T2 | AI crawlers (Perplexity, Bytespider, Amazon, Common Crawl, Meta, Cohere) | Allow but rate-limited (15 req/30 s per IP); GPTBot and ClaudeBot currently denied outright |
| T3 | Known-bad named crawlers (SEO tools, GraphEmbedScraper) | Deny 403 by user agent |
| T4 | Everything else, including the anonymous browser-spoofing farm | Covered by the per-IP rate rules — the only enforcement that touches them |
A crawler that spoofs a normal browser user agent cannot be blocked by any user-agent rule; only the per-IP behavioural rate rules hold it. That is why T4 exists and why the rate rules are the real enforcement layer.
The rules, in plain language
Section titled “The rules, in plain language”All rules run at request-header time (phase 1). IDs 10xxxxx are estate flood rules, 8834xxxx are bot-policy rules, 883491xx are hygiene.
| Rule | Tier | What it does | Response |
|---|---|---|---|
| 88349100 | hygiene | Blanks the Authorization header from every log and audit entry so client credentials never get written to disk |
pass |
| 1000199 | T1 | Client IP is on the verified search-crawler list → skip every flood rule for this request | pass, logged |
| 1000196 | T2 | Client IP is on the verified AI-bot list → skip every flood rule (but not the AI rate limit) | pass, logged |
| 1000198 | backend | Requests to /wp-admin/, /wp-json/, /wp-login.php skip the flood rules (the site’s own admin/AJAX traffic is never rate-limited) |
pass |
| 88345501 | T3 | User agent is GPTBot / OAI-SearchBot / ChatGPT → blocked (AI-training opt-out) | 403 |
| 88345502 | T3 | User agent is ClaudeBot → blocked | 403 |
| 1000201 | counter | Any URL carrying a WooCommerce facet/shopping parameter bumps this IP’s counter (resets after 60 s). Also counts wc-ajax=add_to_cart (the modern AJAX cart vector) |
pass |
| 1000200 | T4 | More than 15 counted facet requests from one IP in 60 s → rate-limited. The main lever that held the 31 July flood | 429 |
| 1000202 | T4 | Facet endpoint with no cookie at all → a bot or scanner | 429 |
| 1000203 | T4 | Facet endpoint with a cookie that is not a session cookie (name lacks woocommerce/wordpress/wp-/phpsessid) | 429 |
| 1000210 | T4 | /cart/ or /checkout/ URL carrying add-to-cart anywhere in the query → the cart-page flood vector |
403 |
| 88345503 | T3 | User agent is GraphEmbedScraper (price-data scraper) → blocked | 403 |
| 88345504 | T3 | User agent is an SEO-tool crawler (ahrefs, Semrush, MJ12, DotBot, petalbot, DataForSEO, Serpstat, SEOKicks, Scorpy, Magpie) → blocked | 403 |
| 88345505 | counter | User agent is an allowed AI crawler → bumps this IP’s counter (resets after 30 s) | pass |
| 88345506 | T2 | More than 15 AI-crawler requests from one IP in 30 s → rate-limited | 429 |
Why 429 for rate limits and 403 for denies: 429 (Too Many Requests) is the signal that tells a well-behaved client to slow down and back off, which is what you want from a rate limit. 403 is a straight refusal for named bad actors.
Deploying the rules
Section titled “Deploying the rules”The rule set is deployed through the control panel’s own custom-directives channel, never by editing the generated ModSecurity config file by hand. The generated file is rebuilt from the GUI on every save, so a manual edit is overwritten. The deploy and verify steps are panel-specific:
- Plesk: Deploy the Noiz ModSecurity Rules on Plesk (Websites & Domains → Web Application Firewall → Custom directives).
- ISPConfig: Deploy the Noiz ModSecurity Rules on ISPConfig (Sites → the website → Options → Apache directives).
Reading the audit log
Section titled “Reading the audit log”ModSecurity’s audit log (/var/log/modsec_audit.log) records every rule
match. One important distinction: a rule match is not a block. For
example, rule 1000202 (no-cookie facet endpoint) writes an audit entry for
every facet request it sees; only the transactions that also failed the
second condition of the chain got a 429. Count the 429s in the access log,
not the rule matches in the audit log, when measuring blocking.
Maintenance
Section titled “Maintenance”Four things keep the rule set honest:
- Allowlist refresh, monthly. The search-engine and AI-bot allowlists are full bypasses for whatever IPs they contain, and crawler ranges change. Re-verify each entry monthly with reverse-DNS + forward-DNS (hostname must be under the vendor’s domain and resolve back to the same address). Remove entries that fail either leg, apply the new list with a diff — never blindly — and record the check date in the file header.
- Geo database refresh, monthly. The flood-identification tool reads a
country database for crawler attribution; refresh it on the same monthly
cadence with
npulse geoip-update. - Threshold review. The AI-crawler rate limit and the facet counter thresholds were calibrated from incident evidence. Re-examine them periodically from the audit log; if a legitimate crawler trips a rate limit, raise it with evidence.
- Collection-file compaction. Every counting rule in this set writes to ModSecurity’s persistent collections, and those files grow without limit and never reclaim space on their own. Measure the fragmentation rate rather than the file size, and rebuild the collections when it climbs. See Why ModSecurity collection files grow without limit, and how to compact them.
Known limits (deliberate or accepted)
Section titled “Known limits (deliberate or accepted)”- Cookie rules check names, not sessions. 1000202/1000203 look at whether
a cookie exists and whether its name looks like a session cookie — they
cannot ask PHP whether a session is actually live. A bot that sends a
cookie named
woocommerce_cart_hashwith garbage content passes. This is an accepted risk for now; the real fix if the crawler farm escalates is headless-bot detection or application-level cart-hash validation. - The anonymous farm is only held by the rate rules. A headless Chrome farm with one request per IP per window is below every per-IP threshold; the 1000200 family catches the floods, not the slow crawl. Density-based or edge (CDN) detection is the escalation path.
- WordPress backend exemption is wide. Everything under
/wp-json/is exempt from the flood rules; REST API scraping is not rate-limited by this set. Tracked as an open item. xmlrpc.phpis unguarded by this set (Jetpack dependency varies per site); pingback reflection abuse is covered elsewhere if enabled.

