Conversation
Covers two migration strategies (start fresh vs carry over), safe production cut-over phases (parallel detection, validation, cut-over), common false positive areas after the migration, and the new per-PL reporting model for systematic tuning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sets refLinksErrorLevel=WARNING so cross-post ref links don't break the build when sibling posts don't yet exist on the same branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The author is already shown from the front matter. Adds the related-pages shortcode to cross-link migration series posts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a new blog post (Part 6) describing two CRS 4 false-positive tuning strategies, a three-phase production migration workflow, common post-migration false-positive categories, per-paranoia-level scoped exclusion techniques with an example SecRule, and CRS 4.25.0 LTS maintenance notes; links to Part 7. ChangesCRS Migration Part 6 Blog Post
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Microsoft Presidio Analyzer (2.2.362)content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.mdMicrosoft Presidio Analyzer failed to scan this file Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
Deploying website with
|
| Latest commit: |
2130fd3
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://97dd8466.website-1u6.pages.dev |
| Branch Preview URL: | https://blog-crs-migration-part-6.website-1u6.pages.dev |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md (4)
16-17: 💤 Low valueMinor editorial: consider removing duplicate/indirect framing.
The opening references “Part 6 … tuning phase” and also points back to Part 5; that’s good, but it may be slightly redundant with the next-line “This post covers the tuning phase itself…” framing. A small tightening would reduce scroll friction.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md` around lines 16 - 17, Tighten the duplicate framing in the intro by removing or rephrasing the second sentence ("This post covers the tuning phase itself — the practical work of establishing a clean CRS 4 baseline for your applications.") so it doesn't repeat the opener ("This is Part 6..."). Edit the sentence containing "This post covers the tuning phase itself" to either merge it with the Part 6 line or replace it with a brief lead-in that adds new value (e.g., a one-line summary of the post's specific goal), updating the lines that contain "This is Part 6..." and "This post covers the tuning phase itself" accordingly.
68-92: ⚡ Quick winSoften fixed time windows into “coverage-based” criteria.
The post uses fixed durations (e.g., “1–2 weeks”, “at least two weeks”, “one to two weeks”, and “first week after cut-over”). In practice, traffic patterns differ (seasonality, rollout timing, low-traffic endpoints), so a reader might disable enhanced logging/exclusions before all workflows have been exercised. Consider adding a brief criterion such as “until you’ve observed peak traffic + all critical workflows at least once” (or “one full business cycle”) while keeping the suggested defaults as examples.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md` around lines 68 - 92, Update the fixed-duration recommendations in the Phase 1/Phase 2/Phase 3 guidance to include a coverage-based stopping criterion: keep the example durations (e.g., “1–2 weeks”, “first week after cut-over”) but add a sentence such as “or until you’ve observed peak traffic and all critical workflows at least once (one full business cycle),” and apply this to the detection phase, the validation phase, and the post-cutover logging guidance; edit the headings/paragraphs referencing “detection mode”, “anomaly threshold 9999”, and the cut-over week to incorporate the new coverage-based criterion while retaining the example time windows as defaults.
103-106: 💤 Low valueNarrow the “PL redistribution” explanation to avoid overgeneralization.
“New firings are most common in SQL injection and XSS detection rules” is plausible, but it’s still a broad expectation. Readers may over-focus their triage and miss other classes (e.g., request-method/header handling rules). Consider phrasing this as “often includes …” and adding a tip to sort/frequency-rank by rule ID firing counts before deciding where to tune first.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md` around lines 103 - 106, Revise the "PL redistribution" paragraph to avoid overgeneralization: change "The new firings are most common in SQL injection and XSS detection rules." to a softer phrasing like "New firings often include SQL injection and XSS detection rules, but can affect other categories (for example, request-method or header-handling rules)." Then add a concise tip sentence recommending readers frequency-rank or sort rule-ID firing counts before tuning (e.g., "Tip: frequency-rank rule ID firing counts to prioritize tuning efforts"). Update the "PL redistribution" heading paragraph and include the brief ranking tip so readers don’t over-focus on only SQLi/XSS.
115-126: ⚡ Quick winMake the SecRule exclusion example slightly more robust/readable for copy-paste.
The example is internally consistent, but copy/paste readers may miss subtle ordering/placement requirements. You already mention the filename (
REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf), but consider also adding one extra line in the example comment like “Load this before CRS core rules” and/or clarifying why the actions arepass,nolog(it’s an exclusion ctl, not a detection rule). This reduces the chance of someone accidentally deploying it in the wrong include file or expecting it to “block” something.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md` around lines 115 - 126, Update the SecRule example to make placement and intent explicit: in the comment above the rule (in REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf) add a note like “Load this file before CRS core rules” and a short explanation that the actions pass,nolog are used because this is an exclusion ctl (ctl:ruleRemoveTargetById=941100;ARGS:content) not a blocking detection rule; keep the SecRule signature (SecRule REQUEST_URI "@beginsWith /editor/save" "id:1001,phase:2,pass,nolog,ctl:ruleRemoveTargetById=941100;ARGS:content") but ensure the example comment clarifies file ordering and the reason for pass,nolog so copy-paste users won’t place it after the CRS core rules or expect it to block requests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md`:
- Around line 16-17: Tighten the duplicate framing in the intro by removing or
rephrasing the second sentence ("This post covers the tuning phase itself — the
practical work of establishing a clean CRS 4 baseline for your applications.")
so it doesn't repeat the opener ("This is Part 6..."). Edit the sentence
containing "This post covers the tuning phase itself" to either merge it with
the Part 6 line or replace it with a brief lead-in that adds new value (e.g., a
one-line summary of the post's specific goal), updating the lines that contain
"This is Part 6..." and "This post covers the tuning phase itself" accordingly.
- Around line 68-92: Update the fixed-duration recommendations in the Phase
1/Phase 2/Phase 3 guidance to include a coverage-based stopping criterion: keep
the example durations (e.g., “1–2 weeks”, “first week after cut-over”) but add a
sentence such as “or until you’ve observed peak traffic and all critical
workflows at least once (one full business cycle),” and apply this to the
detection phase, the validation phase, and the post-cutover logging guidance;
edit the headings/paragraphs referencing “detection mode”, “anomaly threshold
9999”, and the cut-over week to incorporate the new coverage-based criterion
while retaining the example time windows as defaults.
- Around line 103-106: Revise the "PL redistribution" paragraph to avoid
overgeneralization: change "The new firings are most common in SQL injection and
XSS detection rules." to a softer phrasing like "New firings often include SQL
injection and XSS detection rules, but can affect other categories (for example,
request-method or header-handling rules)." Then add a concise tip sentence
recommending readers frequency-rank or sort rule-ID firing counts before tuning
(e.g., "Tip: frequency-rank rule ID firing counts to prioritize tuning
efforts"). Update the "PL redistribution" heading paragraph and include the
brief ranking tip so readers don’t over-focus on only SQLi/XSS.
- Around line 115-126: Update the SecRule example to make placement and intent
explicit: in the comment above the rule (in
REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf) add a note like “Load this file
before CRS core rules” and a short explanation that the actions pass,nolog are
used because this is an exclusion ctl
(ctl:ruleRemoveTargetById=941100;ARGS:content) not a blocking detection rule;
keep the SecRule signature (SecRule REQUEST_URI "@beginsWith /editor/save"
"id:1001,phase:2,pass,nolog,ctl:ruleRemoveTargetById=941100;ARGS:content") but
ensure the example comment clarifies file ordering and the reason for pass,nolog
so copy-paste users won’t place it after the CRS core rules or expect it to
block requests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9fc19c04-e0b2-4dc0-9e23-390368499184
⛔ Files ignored due to path filters (1)
static/images/2026/04/pexels-northern-29268627.jpgis excluded by!**/*.jpg
📒 Files selected for processing (1)
content/blog/2026-05-04-migrating-from-crs-3-to-crs-4-part-6-tuning.md
|
|
||
| There are two approaches to handling false positive tuning during the migration. Neither is universally better — choose based on the size and complexity of your existing setup. | ||
|
|
||
| ### Strategy A: Start Fresh |
There was a problem hiding this comment.
A think these two subsections are a bit misleading. Reading them, I come to the conclusion that I will have to run detection mode without an active WAF, i.e., replace CRS 3 with 4 immediately. Only when reading "Running a Safe Production Migration" does it become clear that it's possible to run both versions in parallel. I think that needs to be part of the introduction: you can migrate without having to turn off your WAF.
|
|
||
| **Dual WAF deployment.** Route a copy of your traffic (via mirroring or a dedicated logging endpoint) through CRS 4 in detection mode, while CRS 3 continues blocking for real traffic. Compare the CRS 4 detection results against known good requests in your access logs. | ||
|
|
||
| **Single WAF, detection mode.** If a parallel setup is not practical, swap CRS 3 for CRS 4 in pure detection mode. You lose blocking during this phase, so only do this if your threat model allows a temporary blocking gap (e.g. you have another upstream protection layer). |
There was a problem hiding this comment.
This isn't parallel detection. Either rename the section or split into a separate section.
|
|
||
| **Single WAF, detection mode.** If a parallel setup is not practical, swap CRS 3 for CRS 4 in pure detection mode. You lose blocking during this phase, so only do this if your threat model allows a temporary blocking gap (e.g. you have another upstream protection layer). | ||
|
|
||
| **WAF-engine anomaly logging.** Some WAF engine setups allow you to log anomaly score results without blocking, even when blocking is technically enabled, by setting the anomaly threshold very high (e.g. `9999`). Set your CRS 4 thresholds to `9999` during the detection phase so that nothing is blocked even if the score exceeds the normal threshold. |
There was a problem hiding this comment.
It is not clear how this is meant to work. Would CRS 3 continue to run with CRS 4 running in the same engine? Probably not, since that would lead to rule ID conflicts. So is this an alternative to "detection only mode" when that is not supported by the engine?
| **Single WAF, detection mode.** If a parallel setup is not practical, swap CRS 3 for CRS 4 in pure detection mode. You lose blocking during this phase, so only do this if your threat model allows a temporary blocking gap (e.g. you have another upstream protection layer). | ||
|
|
||
| **WAF-engine anomaly logging.** Some WAF engine setups allow you to log anomaly score results without blocking, even when blocking is technically enabled, by setting the anomaly threshold very high (e.g. `9999`). Set your CRS 4 thresholds to `9999` during the detection phase so that nothing is blocked even if the score exceeds the normal threshold. | ||
|
|
There was a problem hiding this comment.
I think the migration option that Fränzi presented should be the recommended one, even though it's more complicated. Did you omit it on purpose?
There was a problem hiding this comment.
Well, that is an official blogpost from a company, not CRS 🤷 . Are we making the official migration recommendation something outside CRS?
There was a problem hiding this comment.
No, I'm not saying to point there, but to describe the method. It's not like the method is patented. It's just a bit of extra work, but at the huge benefit that you can run both rulesets at the same time. Of course, we should talk to Fränzi and Christian first.
|
|
||
| **Response-phase web shell rules.** Applications that return debugging output or verbose error messages containing shell-like text. Check your application's error pages, debug endpoints, and admin panels. | ||
|
|
||
| **Restricted headers — basic list.** Requests that include `Content-Encoding`, `X-HTTP-Method-Override`, or `Expect`. These are blocked at all paranoia levels. Common sources: upload clients that set `Content-Encoding`, JavaScript frameworks using `X-HTTP-Method-Override` for form method override, and .NET clients using `Expect: 100-continue`. |
There was a problem hiding this comment.
IIRC, at least for .NET that behaviour is configurable. Maybe at least mention that possibility here. Seeing such a header does not necessarily mean that a change to the WAF is necessary, maybe the application can be configured differently.
| # In REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf | ||
| # Exclude rule 941100 (XSS via libinjection) for the /editor/save path | ||
| SecRule REQUEST_URI "@beginsWith /editor/save" \ | ||
| "id:1001,phase:2,pass,nolog,\ |
There was a problem hiding this comment.
Shouldn't this rule be in phase 1?
- Clarify that migration strategies are independent of rollout, and you do not have to turn off your WAF to validate CRS 4 - Restructure Phase 1 detection options: rename to "Detection-Mode Validation" and label dual-WAF as the only true parallel option; clarify single-WAF and anomaly-threshold-override approaches swap CRS 3 for CRS 4 (no rule ID conflicts) and call out the anomaly-threshold trick as a fallback when the engine has no detection-only switch - Suggest configuring the client (e.g. .NET Expect: 100-continue) before adding a WAF exclusion for restricted headers - Move SecRule exclusion example from phase 2 to phase 1
Summary by CodeRabbit