<p>As firewall rule sets grow in complexity, security teams face a common challenge: manual log analysis is used to determine which rules are actively matching traffic and which are consuming capacity without being triggered. This lack of visibility creates operational and compliance gaps. Organizations with governance policies that require removal of dormant rules after a defined period have no mechanism to identify them. Teams responsible for compliance frameworks such as Payment Card Industry (PCI) 4.0 and Digital Operational Resilience Act (DORA) can’t provide evidence that specific controls are actively functioning. Central teams managing firewalls on behalf of multiple business units have no way to determine which rules are unused or need updating.</p>
<p>In this post, you learn how a new <span class="LinkEnhancement"><a class="Link" href="https://aws.amazon.com/network-firewall/" target="_blank" rel="noopener" data-cms-ai="0">AWS Network Firewall</a></span> capability—rule hit count—addresses these challenges by providing traffic match data for stateful rules across both custom and managed rule groups. With this data, you can identify and remove unused rules, accelerate incident response, and validate security control effectiveness for compliance.</p>
<div class="RichTextHeading">
<h2><b>How it works</b></h2>
</div>
<p>Rule hit counts track how often each stateful rule matches network traffic. The hit counter increments only when a rule match results in an alert log being created. This means any rule with an <code class="CodeInline" style="color: #000">alert</code>, <code class="CodeInline" style="color: #000">drop</code>, or <code class="CodeInline" style="color: #000">reject</code> action will increment the hit counter, because these actions generate alert logs. However, rules configured with a <code class="CodeInline" style="color: #000">pass</code> action don’t generate alert logs by default, meaning they won’t appear in the rule hit count metric.</p>
<p>To gain visibility into traffic matching pass rules, you can include the <code class="CodeInline" style="color: #000">alert</code> keyword within the <code class="CodeInline" style="color: #000">pass</code> rule. This generates an alert log while still permitting the traffic to its intended destination. The following Suricata rule demonstrates this approach:</p>
<div class="Enhancement" data-align-center="">
<div class="Enhancement-item">
<div class="CodeBlockWP hide-language">
<div class="code-toolbar">
<pre class="unlimited-height-code language-text"><code class="language-text">pass tls $HOME_NET any -> $EXTERNAL_NET 443 (msg:"Pass and Log HTTPS traffic"; alert; sid:1000001; rev:1;)</code></pre>
</div>
</div>
</div>
</div>
<p>This rule passes HTTPS traffic to its destination while also generating an alert log, making sure the rule appears in the hit count metric.</p>
<p>The rule hit count feature adds the following metadata to each alert log. Metadata is included by default and doesn’t require additional configuration:</p>
<p><code class="CodeInline" style="color: #000">“aws_metadata": { “resource_arn": “arn:aws:network-firewall:us-east-1:123456789012:stateful-rulegroup/StatefulRuleGroup” }</code></p>
<p>The following example shows a complete alert log with this metadata included:</p>
<div class="Enhancement" data-align-center="">
<div class="Enhancement-item">
<div class="CodeBlockWP hide-language">
<div class="code-toolbar">
<pre class="language-text"><code class="language-text">{
"firewall_name": "egress-and-east-west-firewall",
"availability_zone": "us-east-1a",
"event_timestamp": "1786112515",
"event": {
"tx_guessed": true,
"aws_category": "",
"tx_id": 0,
"app_proto": "http",
"ip_v": 4,
"src_ip": "10.2.1.205",
"src_port": 46240,
"event_type": "alert",
"alert": {
"severity": 3,
"signature_id": 10000003,
"rev": 0,
"signature": "Egress HTTP but not port TCP/80",
"action": "blocked",
"category": ""
},
"ts_progress": "request_complete",
"flow_id": 927132830538451,
"dest_ip": "3.226.253.175",
"proto": "TCP",
"verdict": {
"action": "drop"
},
"http": {
"hostname": "3.226.253.175",
"http_port": 4444,
"url": "/",
"http_user_agent": "curl/8.17.0",
"http_method": "GET",
"protocol": "H