Skip to content

Creating Firewall Rules

💡
Before you start

🔴 This page is about Windows, and steps 1–3 need Linux. That is deliberate: Windows Firewall gives you no way to watch rule precedence happen, and the fastest way to understand what it is protecting you from is to feel the alternative. A VM or WSL is enough.

For steps 4–5 you need a Windows machine and administrator rights.

You need no firewall experience. One idea carries the lab: when two rules disagree, something has to decide — and different firewalls decide differently.

What Are Firewall Rules?

Windows Firewall rules are instructions that tell the firewall whether to allow or block specific network traffic. Rules can be based on programs, ports, protocols, or IP addresses.

By creating custom rules, you gain granular control over exactly which applications can communicate over the network and on which ports.

💡
Inbound vs Outbound

Inbound rules control traffic coming INTO your computer. Outbound rules control traffic going OUT from your computer to the network or internet.

Opening Advanced Firewall Settings

The basic Windows Firewall panel has limited options. For creating custom rules, use the advanced interface:

1
Press Win + R to open the Run dialog
2
Type wf.msc and press Enter

This opens "Windows Defender Firewall with Advanced Security" where you can see all inbound and outbound rules.

Creating a Program Rule

To allow or block a specific application:

1
Click "Inbound Rules" in the left panel, then "New Rule..." in the right panel
2
Select "Program" and click Next
3
Browse to the program's .exe file (e.g., C:\Program Files\MyApp\app.exe)
4
Choose "Allow the connection" or "Block the connection"
5
Select which profiles apply (Domain, Private, Public) and give the rule a name

Creating a Port-Based Rule

To control traffic on specific ports:

1
Click "Inbound Rules" then "New Rule..."
2
Select "Port" and click Next
3
Choose TCP or UDP and enter the port number (e.g., 8080) or range (e.g., 3000-3010)
4
Choose the action (Allow or Block) and finish the wizard

Using PowerShell

You can also create rules via PowerShell (run as Administrator):

# Allow inbound TCP port 8080
New-NetFirewallRule -DisplayName "Allow Port 8080" -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow

# Block outbound connections for a program
New-NetFirewallRule -DisplayName "Block MyApp" -Direction Outbound -Program "C:\MyApp\app.exe" -Action Block

# Remove a rule
Remove-NetFirewallRule -DisplayName "Allow Port 8080"

Testing Your Rules

After creating a rule, verify it works:

  • Check the rule appears in the rules list and is enabled (green checkmark)
  • Test the connection the rule affects (try accessing the port or running the program)
  • Use netstat -an in Command Prompt to see active connections and listening ports
  • Temporarily disable the rule to confirm it was actually affecting traffic
⚠️
Be careful with outbound blocks

Blocking outbound traffic for the wrong program can break Windows Update, antivirus updates, or other essential services. Test changes carefully.

Now Do It Yourself: Find Out Which Rule Wins

Every firewall answers one question when two rules disagree: which one wins? Get that wrong and you will write a rule that looks perfect, sits enabled in the list, and does nothing whatsoever. In twenty minutes you can feel that happen on a real firewall — and then see why Windows avoids that particular trap by having a different one.

Steps 1–3 need Linux (a spare machine, WSL, or a VM) and were run to produce every output below — they use real TCP connections against a real kernel firewall, entirely on 127.0.0.1. Steps 4–5 are on Windows and are marked where they could not be re-run here.

1
Build a listener and a way to knock on its door

Go: a Linux terminal — a spare machine, WSL on Windows, or a virtual machine. mkdir fwlab then cd fwlab.

Do: save these two small programs. Save this first one as listen.py — it waits for connections on port 9000:

import socket, threading, time
s = socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 9000)); s.listen(8)
def serve():
    while True:
        try: s.accept()
        except OSError: break
threading.Thread(target=serve, daemon=True).start()
time.sleep(30)

And save this one as probe.py — it tries to make a connection and reports what happened:

import socket, sys
c = socket.socket(); c.settimeout(1.5)
try:
    c.connect(("127.0.0.1", 9000)); print(f"{sys.argv[1]:<34} CONNECTED")
except Exception as e:
    print(f"{sys.argv[1]:<34} BLOCKED ({type(e).__name__})")
finally:
    c.close()

You should see: two files. Nothing runs yet, and nothing leaves your machine — both talk to 127.0.0.1, your own computer.

You now have the two halves of every firewall test: something listening, and something knocking. Without both, you are reading rules rather than testing them — which is how people end up confident about a firewall that is not doing what they think.

If not: Address already in use later on means something else holds port 9000. Change 9000 to another number in both files.

2
Run the whole sequence inside a throwaway network

Go: the same folder.

Do: save this as lab.sh. It builds a private network, then adds firewall rules one at a time and probes after each:

ip link set lo up
python3 listen.py &
LISTENER=$!
sleep 1
python3 probe.py "1. no rules"
iptables -A INPUT -p tcp --dport 9000 -j DROP
python3 probe.py "2. after DROP rule"
iptables -A INPUT -p tcp --dport 9000 -j ACCEPT
python3 probe.py "3. ACCEPT added AFTER the DROP"
iptables -I INPUT 1 -p tcp --dport 9000 -j ACCEPT
python3 probe.py "4. ACCEPT inserted BEFORE it"
echo
iptables -L INPUT -n --line-numbers
kill $LISTENER 2>/dev/null

Run it in an isolated network namespace, which gives you firewall control without touching your real machine and without sudo:

unshare --user --map-root-user --net -- sh lab.sh

You should see: four probes, and the third is the surprise:

1. no rules                        CONNECTED
2. after DROP rule                 BLOCKED (TimeoutError)
3. ACCEPT added AFTER the DROP     BLOCKED (TimeoutError)
4. ACCEPT inserted BEFORE it       CONNECTED

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     6    --  0.0.0.0/0            0.0.0.0/0            tcp dpt:9000
2    DROP       6    --  0.0.0.0/0            0.0.0.0/0            tcp dpt:9000
3    ACCEPT     6    --  0.0.0.0/0            0.0.0.0/0            tcp dpt:9000

Nothing was simulated. Those are real TCP connections meeting a real kernel firewall.

If not: unshare: Operation not permitted means unprivileged user namespaces are disabled. On Debian or Ubuntu check sysctl kernel.unprivileged_userns_clone. If every probe says CONNECTED, the iptables lines failed silently — run the script again and read the output above the probes.

3
Look at line 3 until it bothers you

Go: the output you just produced.

Do: compare probes 2, 3 and 4 against the rule list underneath. Rule 3 is an ACCEPT for exactly the port being tested, and it sits in the table the whole time probe 3 runs.

You should see: an ACCEPT rule that does nothing at all. The traffic was already dropped by rule 2 before the kernel reached rule 3.

🔴 This firewall reads top to bottom and stops at the first rule that matches. A permission placed below a block is not a weaker permission — it is unreachable code. Probe 4 shows the same rule, unchanged, working perfectly once it sits above the block. The rules did not change. Only their order did.

If not: if probes 3 and 4 give the same answer, the -I INPUT 1 line ran as -A-A appends to the end, -I INPUT 1 inserts at position 1. That one letter is the entire lesson.

4
Now find out which model Windows uses — because it is not this one

Go: a Windows machine, and open Windows Defender Firewall with Advanced Security (press Win+R, type wf.msc, press Enter).

Do: click Inbound Rules and look at the list. Note what is not there: any way to reorder the rules. There is no move-up button and no rule numbers.

You should see: a sortable table with no inherent order — because Windows does not resolve conflicts by position.

🔴 Windows uses a precedence model, not an order model. Microsoft documents the ranking: explicit block rules beat explicit allow rules, whatever their position in the list. So the trap you just felt on Linux — a permission hidden under a block — cannot happen here. A different trap replaces it: an allow rule that appears correct, is enabled, and never takes effect because some block rule elsewhere in a list of hundreds outranks it, with nothing in the interface pointing at which one.

This step needs Windows and was not re-run for this page. Steps 1–3 were, on Linux, and they are what the reasoning rests on — the point is not that the two behave the same, but that they behave differently and you must know which you are in.

If not: if wf.msc does not open, you are on Windows Home with a restricted policy, or not an administrator. The simple Firewall panel in Settings cannot show rule precedence at all — it is exactly the view that hides this.

5
Ask your own firewall which rule actually decided

Go: a Windows PowerShell window opened as Administrator.

Do: list every enabled inbound block rule — the ones that outrank your allows and are the usual reason a rule “does not work”:

Get-NetFirewallRule -Direction Inbound -Enabled True -Action Block | Select-Object DisplayName, Profile

You should see: the block rules currently in force. Any allow rule you create that overlaps one of these will lose, no matter where it appears in the list or when you created it.

Not re-run for this page — PowerShell is Windows-only and this page was written on Linux. The command is from Microsoft's documented NetSecurity module.

Whichever system you are on, the habit from step 1 is the one that transfers: after writing a rule, test it with something that actually connects. A rule that appears in a list has not been shown to work; a connection that succeeds or fails has.

If not: Get-NetFirewallRule is not recognized means PowerShell is running as an older version or you are in Command Prompt rather than PowerShell. The older equivalent is netsh advfirewall firewall show rule name=all, which prints far more and is harder to read.

🎉
Check yourself before moving on

You add an inbound allow rule on Windows for your app on port 8080. It is enabled, the port is right, and the app still cannot be reached. From step 4, what is the most likely cause — and why would moving the rule up the list not help? Answer: An enabled block rule somewhere else overlaps port 8080, and on Windows block beats allow regardless of position. Moving it would not help because Windows has no rule order to move it within — that is precedence, not sequence. Find the offending block rule with the command in step 5.

Now do it without the page: on any firewall you use, write one rule and then prove it works with a real connection rather than by reading the rule list. If you can state which model that firewall uses — first match, or block-wins — you can predict its behaviour instead of testing hopefully.

Summary

In this tutorial, you learned:

  • The difference between inbound and outbound firewall rules
  • How to open the Advanced Firewall interface
  • Creating rules based on programs and ports
  • Managing rules with PowerShell commands
  • How to test and verify your firewall rules
🎉
You now have granular control!

Custom firewall rules give you precise control over which programs and ports can communicate on your network.