Windows Firewall logging and monitoring that works
Turn Windows Firewall into a detection source: configure pfirewall.log, WFP audit events 5156/5157, rule-change events 4946-4950 and forward them centrally.
A host firewall that blocks traffic but tells nobody is only half a control. The drops it records show scanning, lateral movement attempts and misconfigured applications. Its configuration-change events show an attacker opening a port for their tooling or switching the firewall off. Most environments collect neither.
The Windows Firewall hardening pillar sets the policy: all profiles on, inbound default block, admin protocols scoped to management hosts. This guide builds the monitoring on top of it. There are three sources, and each has a different purpose:
| Source | Where | Best for |
|---|---|---|
pfirewall.log | Text file on each host | Local investigation, building allow lists |
| Windows Filtering Platform (WFP) audit events | Security log, 5150-5159 | Central detection of blocked and allowed connections, with process path |
| Firewall policy-change events | Security log, 4944-4958 | Detecting tampering with rules and settings |
The broader area overview lives on the Windows Firewall topic page.
Step 1: configure pfirewall.log per profile
GPO path: Computer Configuration > Policies > Windows Settings > Security Settings > Windows Defender Firewall with Advanced Security. Open the properties, and on each of the Domain Profile, Private Profile and Public Profile tabs, click Customize under Logging:
| Setting | Recommended value |
|---|---|
| Name | %SystemRoot%\System32\LogFiles\Firewall\pfirewall.log (default) |
| Size limit (KB) | 16,384 to 32,767 (32,767 is the maximum) |
| Log dropped packets | Yes |
| Log successful connections | No on workstations; Yes on sensitive servers or during rule discovery |
When the file reaches its size limit, Windows renames it to pfirewall.log.old and starts a new one, overwriting the previous .old. On a noisy server that can mean only a few hours of history, so either collect the file or rely on the events in Step 2.
You can use a separate file per profile (domainfw.log, publicfw.log) to see at a glance which profile a laptop was in when something was dropped. If you move the log anywhere other than the default folder, the firewall service must be able to write there:
$path = 'D:\Logs\Firewall'
New-Item -ItemType Directory -Path $path -Force | Out-Null
$acl = Get-Acl $path
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
'NT SERVICE\mpssvc','Modify','ContainerInherit,ObjectInherit','None','Allow')
$acl.AddAccessRule($rule); Set-Acl -Path $path -AclObject $aclIn Intune, the equivalent profile settings are in the settings catalog under the Firewall category, per profile: Log File Path, Log Max File Size, Enable Log Dropped Packets and Enable Log Success Connections (Firewall CSP MdmStore/DomainProfile/LogFilePath, LogMaxFileSize, EnableLogDroppedPackets, EnableLogSuccessConnections, and the same under PrivateProfile and PublicProfile). On a device that also receives Group Policy, a GPO value for the log path and size wins over the MDM value.
Reading the file
The file is space-delimited with a #Fields: header line. Parse it from the header rather than assuming a fixed column order, because newer Windows builds have added fields:
$log = "$env:SystemRoot\System32\LogFiles\Firewall\pfirewall.log"
$fields = (Get-Content $log -TotalCount 10 | Where-Object { $_ -like '#Fields:*' }) -replace '^#Fields:\s*' -split ' '
Get-Content $log | Where-Object { $_ -and $_ -notlike '#*' } |
ConvertFrom-Csv -Delimiter ' ' -Header $fields |
Where-Object action -eq 'DROP' |
Group-Object 'src-ip','dst-port' | Sort-Object Count -Descending | Select-Object -First 20 Count, NameA single source hitting many ports, or many internal sources hitting TCP 445 or 5985 on workstations, is exactly the pattern the pillar's rules are designed to stop.
Step 2: enable Windows Filtering Platform auditing
WFP is the engine underneath the firewall. Its audit events are more useful than the text log for central monitoring because they include the application path and the direction and go through the normal event pipeline.
GPO path: Computer Configuration > Policies > Windows Settings > Security Settings > Advanced Audit Policy Configuration > Audit Policies > Object Access:
| Subcategory | Setting | Events |
|---|---|---|
| Audit Filtering Platform Connection | Failure (fleet-wide); Success only on selected servers | 5157 blocked connection, 5159 blocked bind, 5155 blocked listen; 5156, 5158, 5154 on success |
| Audit Filtering Platform Packet Drop | Failure on selected servers only | 5152 packet dropped, 5153 |
Remember that Advanced Audit Policy only applies if Audit: Force audit policy subcategory settings to override audit policy category settings is enabled under Security Options, which any modern baseline already does.
On a single machine for testing:
auditpol /set /subcategory:"Filtering Platform Connection" /failure:enable
auditpol /get /subcategory:"Filtering Platform Connection","MPSSVC Rule-Level Policy Change"Event 5157 reads "The Windows Filtering Platform has blocked a connection" and contains the process (as a device path such as \device\harddiskvolume3\windows\system32\svchost.exe), direction, source and destination address and port, protocol and a Filter Run-Time ID. To find which filter or rule produced a block, dump the WFP state and search for that ID:
netsh wfp show filters file="$env:TEMP\filters.xml"
Select-String -Path "$env:TEMP\filters.xml" -Pattern '<filterId>123456</filterId>' -Context 0,20Note that 5157 also fires for packets dropped by the default block action, not only for explicit block rules, so a stream of 5157 events is normal. What matters is who is being blocked and from where.
Step 3: always audit firewall configuration changes
Configuration changes are rare, cheap to collect and high-value. Enable Audit MPSSVC Rule-Level Policy Change for Success and Failure under Advanced Audit Policy Configuration > Audit Policies > Policy Change. The key events in the Security log:
| Event | Meaning |
|---|---|
| 4944 / 4945 | Active policy and rules listed when the firewall service started |
| 4946 | A rule was added to the exception list |
| 4947 | A rule was modified |
| 4948 | A rule was deleted |
| 4949 | Firewall settings restored to defaults |
| 4950 | A firewall setting was changed (for example the firewall was turned off in a profile) |
| 4954 | Group Policy firewall settings changed and were applied |
| 4956 | The active profile changed |
Group Policy refresh produces 4954 and, on some builds, bursts of 4946/4948 as policy rules are re-applied. Build your detection around changes that do not coincide with a policy refresh, or that come from a process other than the ones your management stack uses.
The Applications and Services Logs > Microsoft > Windows > Windows Firewall With Advanced Security > Firewall log records the same changes with rule details and the modifying application. Its event IDs differ between Windows releases (older builds used 2004, 2005 and 2006 for added, modified and deleted rules), so check the IDs on your current Windows 11 24H2/25H2 and Server 2025 images before writing rules against it.
Also watch the service itself. An attacker who stops MpsSvc or changes its start type leaves Service Control Manager events 7036 and 7040 in the System log.
Step 4: forward what matters
For the WFP and policy-change events, Windows Event Forwarding (see the WEF glossary entry) or your SIEM agent is the transport. Keep the subscription tight so 5157 does not drown the collector:
<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">*[System[(EventID>=4946 and EventID<=4950) or EventID=4954 or EventID=4956]]</Select>
<Select Path="Security">*[System[EventID=5157]] and *[EventData[Data[@Name='Direction']='%%14592']]</Select>
</Query>
</QueryList>%%14592 is the inbound direction value. Blocked inbound connections on workstations are the interesting ones for lateral movement; blocked outbound is noisier and mostly useful on servers with default-deny outbound. If volume is still too high, collect 5157 only from servers and a sample of workstations, and rely on EDR network telemetry for the rest.
For pfirewall.log, point your log agent at the file (and the .old file) and parse it with the header fields shown above.
Detection ideas
- Firewall turned off or default inbound set to Allow: 4950 for any profile, outside a change window. Alert immediately.
- New inbound allow rule created locally: 4946 on a device where local rule merge is disabled is often an attacker or installer that will have no effect, but it still signals intent.
- Internal scanning: one source address producing 5157 events against many destination hosts or ports within minutes.
- Workstation-to-workstation management traffic: 5157 inbound on TCP 445, 135, 3389 or 5985 from non-admin subnets.
- Blocked outbound SMB to the internet: an attempted forced authentication or data exfiltration.
The security event ID reference puts these alongside the logon and process events you will correlate them with.
What breaks
- Security log churn: enabling Filtering Platform Connection success auditing fleet-wide can rotate the Security log in minutes and hide logon events. Increase the Security log size (at least 1 GB on servers) and stick to failures on clients.
- Collector load: an unfiltered 5157 subscription is often the single largest event source in a WEF deployment. Filter at the source with XPath.
- Custom log paths without
NT SERVICE\mpssvcwrite access silently produce no file. - Profile mismatch: logging configured only in the Domain profile gives nothing when a laptop is on the Public profile at home.
Verify it
Check the effective logging configuration, which reflects GPO and MDM rather than local settings:
Get-NetFirewallProfile -PolicyStore ActiveStore |
Select-Object Name, LogFileName, LogMaxSizeKilobytes, LogBlocked, LogAllowed
Get-NetConnectionProfile | Select-Object InterfaceAlias, NetworkCategory
auditpol /get /subcategory:"Filtering Platform Connection","MPSSVC Rule-Level Policy Change"Then test end to end: from a non-admin workstation, run Test-NetConnection <target> -Port 445 against another workstation. On the target you should see a DROP TCP line in pfirewall.log and a 5157 event; on the collector, the 5157 should arrive within your forwarding interval. Finally, add and delete a harmless test rule and confirm 4946 and 4948 reach the SIEM.
Checklist
- Dropped-packet logging on in all three profiles, file size at least 16 MB
- Custom log paths writable by
NT SERVICE\mpssvc - Filtering Platform Connection audited for failure; success only on selected servers
- MPSSVC Rule-Level Policy Change audited for success and failure
- Events 4946-4950, 4954 and 5157 (filtered) forwarded to the SIEM
- Alerts on firewall disabled, local allow rules and internal scanning patterns
- End-to-end test performed: blocked connection and rule change visible centrally
Logging is only useful if the policy behind it is consistent across the fleet; the next step is managing firewall policy at scale.
Frequently asked questions
Should I use pfirewall.log or the WFP security events?
Use both for different jobs. pfirewall.log is a cheap, local text file that is excellent during an investigation on one host but awkward to collect at scale. WFP audit events (5157 for blocked connections, 5156 for allowed ones) land in the Security log, carry the process path and travel through Windows Event Forwarding or your agent like any other event. For central detection, prefer the events and filter them hard.
Why is my pfirewall.log empty even though logging is enabled?
The most common causes are a custom log path the firewall service cannot write to, logging enabled in a profile that is not active, or local profile settings being overridden by a GPO that leaves logging off. Check the active store with Get-NetFirewallProfile, confirm which profile the network adapter is in, and grant NT SERVICE\mpssvc write access if you moved the log.
Is auditing successful connections (5156) too noisy?
On a busy server or workstation, yes: it can generate thousands of events per minute and push useful events out of the Security log. Enable it only on a few sensitive servers, or temporarily while building an outbound allow list. For fleet-wide collection, audit failures only (5157) and always audit rule and setting changes, which are rare and valuable.
Windows Firewall logging and monitoring that works