The tunnel is gone, the network icon looks normal, and nothing loads. You disconnected the VPN, or uninstalled it, or rebooted, and the block that was meant to protect you during a drop is still standing. The client window cannot explain it, because the client is not what is blocking you: the decision was written into the kernel in your name, and nothing was left behind to remove it. On Windows that state sits in the Windows Filtering Platform; on Linux it is a rule set in iptables or nftables, often with a snapshot that gets replayed at boot. What follows is the order I use: read the state, delete the narrowest object that explains the failure, then find whatever keeps putting it back.

A kill switch is kernel state, not a setting

Desktop clients implement blocking by adding filters that deny traffic outside the tunnel. On Windows those are WFP filters at the connect-redirect and transport layers, and some of them are persistent: they are recorded in the Base Filtering Engine policy store and restored when the machine boots, before any VPN software runs. Clients differ in when they clean up. Some remove filters on disconnect, some only on a clean exit, and some only when the kill-switch toggle is switched off first. Uninstall while the block is active, or kill the process, and the filters stay.

The Linux equivalent is a drop or reject rule in the output path, sometimes a forwarding rule, sometimes a changed chain policy. Those rules outlive the process that added them because they are kernel state: kill the client, crash it, or cut power, and the kernel keeps dropping packets with nothing left to undo it. A persistence package adds a second layer of permanence by snapshotting the current rule set and restoring it at boot, so the next start begins with the block already installed.

Read the WFP state before deleting anything

Windows ships the tooling you need. Run an elevated prompt and dump the platform state; the dump lands as wfpstate.xml in the working directory.

netsh wfp show state
netsh wfp show filters
Get-Service bfe

Search the XML for the departed vendor's name, for a Block action, and for the layer. Filters on connect-redirect and transport are the ones that break ordinary traffic; a filter on a receive-accept layer usually is not your cause. If the Base Filtering Engine is not running at all, look elsewhere, because nothing else can enforce those filters.

Cross-check the ordinary firewall view as well, since most people inspect only one of the two:

netsh advfirewall show allprofiles
Get-NetFirewallProfile | Select-Object Name,Enabled,DefaultInboundAction

When the built-in profiles are on and no rule matches the failing traffic, the block comes from a third-party provider rather than from Windows Firewall. That distinction decides which tool you will use for the repair.

Read Linux rules in the syntax you will delete with

iptables -S prints rules in the same form used to add them, which makes the output directly reusable for a deletion. iptables -L -v -n adds packet counters, and those counters tell you whether a rule is actually matching your failed requests or merely sitting in the chain.

sudo iptables -S
sudo ip6tables -S
sudo iptables -t nat -S

Read the chain policies, not only the rules: -P OUTPUT DROP blocks traffic even when no rule matches. Inspect IPv6 separately, because a client that installs both families can leave the IPv6 block in place after the IPv4 rules are gone, and the symptom then depends on which family an application prefers. Where nftables is in use, also run nft list ruleset; the two tools coexist through a compatibility layer, and one listing does not necessarily show every rule that can drop a packet.

Delete the narrowest object that explains the failure

On Windows the first step is still the vendor's own removal path: rerun the installer and use repair or uninstall, or run the cleanup tool if the vendor publishes one. Leftover services and scheduled tasks are common, and a task that starts the client at logon reinstalls its filters at the next boot. An uninstall that fails while reading the client's registry key usually means a service or task still holds it, not that the registry is corrupt.

Get-ScheduledTask | Where-Object TaskName -like '*vpn*'
Get-Service | Where-Object DisplayName -like '*vpn*'

Persistent filters are the last resort. They live under HKLM\SYSTEM\CurrentControlSet\Services\BFE\Parameters\Policy\Persistent\Filter, one value per filter keyed by GUID. Export the key first, record the GUIDs you remove, delete only the values that belong to the departed client, then reboot so the engine rebuilds its state. Do not delete the whole key and do not disable the Base Filtering Engine: Windows Firewall and Defender depend on it, and you would trade one outage for a worse one.

reg export "HKLM\SYSTEM\CurrentControlSet\Services\BFE\Parameters\Policy\Persistent" C:\temp\bfe-backup.reg

On Linux, take the line printed by iptables -S and replace -A with -D. Deleting the one rule you identified beats flushing a chain, because it leaves your own policy intact.

sudo iptables -D OUTPUT -p udp -j REJECT

If the drop lives in a user-defined chain, delete the jump to that chain first and remove the chain with -X afterwards. Setting a chain policy to ACCEPT while drop rules are still present only helps packets that reach the policy decision at all. Flushing every table is a last resort: it also removes rules you added for other reasons, and with ufw enabled it leaves the service believing it owns chains that no longer exist.

Find the owner that puts it back

After the immediate repair, look for whatever would restore the state. On Linux, check whether a persistence unit is enabled, whether the client has a restore-firewall-at-login setting, and whether a saved rule file exists under /etc. On Windows, check startup entries, services and scheduled tasks again, then reboot and re-read the state with the same commands as before.

If the block returns, the deletion was not the fix; the owner was. Record which client or service installs it and disable the feature there, instead of fighting it at the firewall layer after every disconnect.

Verify with real traffic, not a single ping

Test a name lookup, a TCP connection and a fresh browser session, then reboot and repeat the same three checks. A ping that works while HTTPS fails points at a transport-layer filter rather than at the network, and a block that reappears only after the reboot points at an owner you have not found yet.

Keep the evidence: the exported BFE key, or the iptables -S output captured before the change. Before calling it fixed, confirm the layer you removed the object from, the owner that could reinstall it, the state after one clean reboot, and that ordinary browsing works with no VPN process running. The iptables(8) manual page has the exact delete and policy syntax if you script the cleanup.

Mikanda mikatakata