⚡ Stop IP Leaks on Windows — Build a VPN Kill-Switch Your VPN Won't Give You

:wrench: Build a Bulletproof VPN Kill-Switch Using Nothing But Windows Firewall + PowerShell

Most VPN clients on Windows are lying to you about leak protection. Here’s how to fix that at the OS level.

Here’s the problem nobody talks about: your VPN drops for 2 seconds while you switch Wi-Fi networks, wake from sleep, or hit a random server hiccup — and for those 2 seconds, every app on your machine blasts your real IP to the internet. OpenVPN’s block-outside-dns breaks on wake-from-sleep. WireGuard’s kill-switch only works with /0 routes. Windows’ built-in VPN has zero leak protection. This guide hands you 4 independent methods to seal those gaps — from zero-install firewall tricks to kernel-level WFP filters.


⚡ Method 1 — Windows Firewall Profile Trick (Zero Tools, 5 Minutes)

The cleanest kill-switch nobody uses. It exploits how Windows Firewall applies different rules to Public vs Private network profiles.

The concept: Force your physical adapter (Ethernet/Wi-Fi) to Public profile → block all outbound by default. Force your VPN adapter to Private profile → allow outbound. VPN drops → traffic has nowhere to go → kill-switch.

Step 1 — Set firewall defaults:

Profile Inbound Outbound
Public Block Block ← this is the kill-switch
Private Block Allow

Step 2 — Assign profiles via PowerShell (Admin):

# Physical adapter → Public (blocked when VPN is down)
Set-NetConnectionProfile -InterfaceAlias "Wi-Fi" -NetworkCategory "Public"

# VPN adapter → Private (allowed when VPN is up)
Set-NetConnectionProfile -InterfaceAlias "VPN Connection" -NetworkCategory "Private"

Step 3 — Allow essential bypass traffic:

You need DHCP and your VPN server IP to punch through the Public block. Create outbound Allow rules for Public profile only:

# Allow DHCP (or you can't get an IP at all)
Enable-NetFirewallRule -DisplayName "Core Networking - Dynamic Host Configuration Protocol (DHCP-Out)"

# Allow VPN server IP through Public profile
New-NetFirewallRule -DisplayName "Allow VPN Server" -Direction Outbound `
  -RemoteAddress "YOUR.VPN.SERVER.IP" -Action Allow -Profile Public

Step 4 — Block the IP ranges that should NEVER bypass VPN:

Create outbound Block rules for Public profile covering all public IP ranges (excluding LAN and VPN server):

Block Range (IPv4) What It Covers
0.0.0.0 - 9.255.255.255 Everything before private ranges
11.0.0.0 - 172.15.255.255 Everything between 10.x and 172.16.x
172.32.0.0 - 192.167.255.255 Everything between 172.31.x and 192.168.x
192.169.0.0 - 255.255.255.254 Everything after 192.168.x

:high_voltage: Pro tip: Block rules override Allow rules in Windows Firewall. Even if some app added its own Allow rule during install, your Block rule wins.

:link: Full walkthrough with IP range math: logrus.dev — Setting up Windows Defender Firewall for VPN kill switch — the only practitioner blog that shows the exact inverted IP ranges for both IPv4 and IPv6.

🔧 Method 2 — SimpleWall (WFP-Based, Open Source, <1MB)

SimpleWall doesn’t touch Windows Firewall at all. It creates its own filters directly in the Windows Filtering Platform — the kernel-level API that even Windows Firewall sits on top of.

Why this is better than Method 1: WFP filters operate below the firewall rule layer. No app can bypass them by adding its own firewall rules. No installer running as Admin can punch a hole. It’s kernel-level enforcement.

Kill-switch setup:

Step Action
1 Install SimpleWall → enable Whitelist mode (blocks everything by default)
2 Allow only your VPN process (e.g. openvpn.exe, wireguard.exe)
3 Allow only the VPN tunnel adapter for all other apps
4 VPN drops → tunnel adapter disappears → all traffic blocked

What makes it special:

Feature Why It Matters
WFP kernel-level filters Can’t be bypassed by user-space apps
Runs alongside Windows Firewall No conflicts — they’re independent systems
Blocks telemetry by default 619+ built-in rules for Microsoft tracking IPs
Portable version available Runs from USB, no install needed
Open source (GPLv3) GitHub — henrypp/simplewall

:warning: Critical gotcha: SimpleWall’s WFP filters are persistent and independent of the app. If you block a critical service and then uninstall SimpleWall, that service stays blocked. Always “Uninstall filters” from the app menu before removing it.

🔒 Method 3 — WireGuard's Built-In Kill-Switch (The /0 Trick + DNS Leak Fix)

WireGuard for Windows has a native kill-switch, but it only activates under specific conditions — and it has a known DNS leak when you DON’T use it.

When the kill-switch activates automatically:

Your WireGuard config needs ALL three conditions:

  1. Only one peer defined
  2. That peer has AllowedIPs = 0.0.0.0/0, ::/0
  3. “Block untunneled traffic” checkbox is enabled (or implied by the /0 route)

What WireGuard’s kill-switch actually does under the hood (WFP rules):

Rule Purpose
Permit WireGuard tunnel service packets VPN traffic itself can flow
Permit DNS only to config-specified DNS servers Blocks Windows’ multihomed DNS resolution
Permit loopback + tunnel interface traffic Local apps and VPN tunnel work
Permit DHCP (v4 + v6) and NDP (v6) Network connectivity basics
Block everything else The kill-switch

:link: Official WireGuard Windows docs — network quirks and kill-switch behavior: git.zx2c4.com/wireguard-windows — netquirk.md

The DNS leak you didn’t know about:

If you use split tunneling (specific AllowedIPs instead of /0), WireGuard does NOT activate the kill-switch. Windows’ default multihomed DNS behavior kicks in — it sends DNS queries to ALL available DNS servers simultaneously, including your ISP’s DNS. Your VPN is up, your traffic is tunneled, but your DNS queries are screaming your browsing history to your ISP.

Fix for split-tunnel DNS leak:

Create firewall rules that only allow UDP port 53 to your VPN’s DNS server:

# Block all DNS except VPN DNS
New-NetFirewallRule -DisplayName "Block non-VPN DNS" -Direction Outbound `
  -Protocol UDP -RemotePort 53 -Action Block -Profile Any

# Allow DNS only to VPN DNS server
New-NetFirewallRule -DisplayName "Allow VPN DNS" -Direction Outbound `
  -Protocol UDP -RemotePort 53 -RemoteAddress "YOUR.VPN.DNS.IP" -Action Allow -Profile Any

The /1 route trick: If you want full-tunnel routing WITHOUT the kill-switch restrictions (e.g., you need LAN access), use AllowedIPs = 0.0.0.0/1, 128.0.0.0/1 instead of 0.0.0.0/0. This routes all traffic through WireGuard via routing table priority but doesn’t trigger the WFP-based kill-switch. Combine with Method 1 or 2 for leak protection.

⚙️ Method 4 — Task Scheduler + PowerShell (Event-Driven Kill-Switch)

For VPNs that don’t support any of the above — the fully DIY approach. Windows logs VPN connect/disconnect events. You catch them and kill the internet.

Key Event IDs:

Event Source Event ID Meaning
RasClient 20225 VPN connected successfully
RasClient 20226 VPN disconnected
NetworkProfile 10000 Network connected
NetworkProfile 10001 Network disconnected
Dhcp-Client 50002 DHCP event (catches VPN drops RasClient misses)

Setup — Task Scheduler:

Step Action
1 Open Task Scheduler → Create Task (not Basic Task)
2 Check “Run with highest privileges”
3 Trigger: On an event → Source: RasClient, Event ID: 20226
4 Action: Start a program → powershell.exe
5 Arguments: -ExecutionPolicy Bypass -File "C:\Scripts\killswitch.ps1"

The PowerShell script (killswitch.ps1):

# Disable the physical network adapter when VPN drops
Disable-NetAdapter -Name "Wi-Fi" -Confirm:$false
Disable-NetAdapter -Name "Ethernet" -Confirm:$false

# Optional: re-enable after delay to allow VPN reconnect
Start-Sleep -Seconds 5
Enable-NetAdapter -Name "Wi-Fi" -Confirm:$false
Enable-NetAdapter -Name "Ethernet" -Confirm:$false

# Optional: restart VPN
rasdial "VPN Connection Name" /disconnect
rasdial "VPN Connection Name"

:warning: Known limitation: There’s a race condition — between the VPN dropping and the Task Scheduler firing (typically 1-3 seconds), traffic can leak. This method is reactive, not proactive. Methods 1-3 are proactive (traffic is blocked by default, only allowed through VPN). Use this as a backup layer, not your only protection.

🚫 IPv6 Leak Vectors — The Holes Everyone Forgets

Your IPv4 kill-switch means nothing if IPv6 is leaking around it. Windows has multiple IPv6 transition technologies enabled by default that can bypass your VPN tunnel entirely.

Kill these immediately (Admin PowerShell):

# Disable Teredo (IPv6-over-IPv4 tunneling — bypasses VPN)
netsh interface teredo set state disabled

# Disable 6to4 (another IPv6 tunnel mechanism)
netsh interface ipv6 6to4 set state state=disabled undoonstop=disabled

# Disable ISATAP (intra-site IPv6 tunneling)
netsh interface ipv6 isatap set state state=disabled

# Nuclear option: disable IPv6 entirely via registry
# (DWORD DisabledComponents = 0xFF at HKLM\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters)

Other leak vectors to close:

Leak Vector Risk Fix
LLMNR Local name resolution bypasses DNS tunnel Disable via Group Policy or REG ADD
NetBIOS over TCP/IP Legacy name resolution, leaks on LAN Disable per-adapter in Advanced TCP/IP settings
Smart Multi-Homed Name Resolution Windows queries ALL DNS servers at once Disable via Group Policy → turn off SMHNR
WebRTC Browser-level IP leak, ignores VPN entirely Browser extension or about:config flag
🧠 How VPN Kill-Switches Actually Work Under the Hood

Every serious VPN kill-switch — whether it’s NordVPN’s, Mullvad’s, or one you build yourself — ultimately uses the Windows Filtering Platform (WFP). Understanding WFP makes all 4 methods above click.

WFP architecture in 30 seconds:

WFP is the kernel-level packet filtering API that sits BELOW Windows Firewall. Windows Firewall is just one of many consumers of WFP. VPN clients, antivirus software, and apps like SimpleWall all create their own WFP filters independently.

Concept What It Does
Layers Inspection points in the network stack (IP, transport, ALE)
Sublayers Independent rule sets that don’t interfere with each other
Filters Rules with conditions + actions (Permit/Block)
ALE layers Application Layer Enforcement — stateful, decides per-connection
ALE reauthorization When rules change, existing connections get re-evaluated

How a WFP kill-switch works (what Tailscale’s engineers documented):

  1. Create a sublayer at maximum weight (0xFFFF) — above Windows Defender (0x1000)
  2. Add a Permit filter for the VPN tunnel interface
  3. Add a Permit filter for the VPN server IP (so encrypted packets can reach it)
  4. Add a Permit filter for DHCP/loopback
  5. Add a Block filter as the catch-all (lowest weight in your sublayer)
  6. ALE reauthorization fires → existing pre-VPN connections get forcibly terminated

Block outranks Permit across sublayers. Your sublayer’s Block verdict can’t be overridden by Windows Defender’s Permit — unless they use a “hard Permit” (which they don’t for general traffic).

:link: Tailscale’s deep-dive on WFP architecture and the kill-switch pattern: tailscale.com/blog/windows-firewall — the best English-language explanation of WFP sublayers, ALE reauthorization, and kill-switch implementation. Written by Tailscale’s networking engineers.

:link: Zero Networks’ WTF-WFP debugging tool: zeronetworks.com — WTF is going on with WFP — open-source PowerShell tool to inspect what WFP filters are actually running on your machine. Want to verify your kill-switch is working? This shows you every active filter, sublayer, and verdict.

:link: Quarkslab’s reverse engineering of WFP persistent state: blog.quarkslab.com — WFP persistent state under the hood — if you want to understand how WFP stores rules that survive reboots (boot-time filters), this is the only deep analysis that exists.


:high_voltage: Quick Hits

Want This Do This
:bullseye: Fastest kill-switch, zero tools → Method 1: Firewall profiles (Public=block, Private=VPN)
:shield: Strongest protection, kernel-level → Method 2: SimpleWall whitelist mode
:wrench: Already using WireGuard → Method 3: AllowedIPs = 0.0.0.0/0 + DNS leak fix
:bell: Event-driven backup layer → Method 4: Task Scheduler on RasClient Event 20226
:prohibited: IPv6 is leaking around everything → Disable Teredo + 6to4 + ISATAP + SMHNR
:brain: Want to verify it’s actually working WTF-WFP tool to inspect active WFP filters

Your VPN is only as strong as what happens when it fails. Now you know what happens — nothing.

9 Likes