In short: the RouterOS firewall filter routes every packet into one of three chains: input (to the router), forward (through the router), output (from the router). In each chain, rules are read from top to bottom, and the first one that matches with an action such as accept or drop ends the process; if none match, the packet passes. This is why a new rule must always be placed above the final drop, never below it.
What are the input, forward, and output chains?
They are three rule lists, one for each direction a packet can take relative to the router. They exist by default and cannot be deleted.
input: packets that enter the router and have one of its addresses as the destination. WinBox, SSH, pings to the router, DNS requests to the router, WireGuard traffic to the router;forward: packets that pass through the router. A LAN PC browsing the web, port forwarding to an internal server;output: packets that originate on the router and exit through an interface. A ping launched from the router terminal, its NTP and DNS requests, the responses the router sends to WinBox.
The point that many miss: packets passing through the router do not pass through the input chain. If you want to prevent a LAN PC from reaching a website, the rule goes in forward. In input, no packet from that PC destined for the internet will ever be seen.
The rules are located in /ip firewall filter for IPv4 and in /ipv6 firewall filter for IPv6: two separate firewalls, with the same three chains. You can also create your own chains (just write a new name in chain=) and jump to them with action=jump; action=return sends it back.
In what order are the rules read?
From top to bottom, in the order you see them with print. The router compares the packet with the first rule in the chain; if it does not match, it moves to the second, and so on. At the first rule that matches, it executes the action and, if the action is terminal, it stops reading that chain.
It does not matter which rule you wrote first; it matters where it is placed. A perfect rule in the wrong place is useless.
Which actions end the process and which do not?
The manual divides them as follows:
- End the chain:
accept(the packet passes, no subsequent rule sees it),drop(dropped silently),reject(dropped with an ICMP response, or a TCP reset withreject-with=tcp-reset),tarpit(keeps the connection hanging); - Continue to the next rule:
log(writes a line to the log),passthrough(just counts),add-src-to-address-listandadd-dst-to-address-list(record the address in a list); - Move the packet:
jumpsends it to another chain,returnsends it back to the chain it jumped from.
This is why port knocking works: the rule that adds you to the address list does not open anything; it records you and lets the packet fall through to the drop. From outside, the port appears closed like the others.
What happens if no rule matches?
The packet is accepted. The default policy of RouterOS chains is accept, and it cannot be changed: if you want to block, you must write the block yourself, as the last rule.
It follows that a router without rules is an open router. After a /system reset-configuration no-defaults=yes (educational reset, see the Basic Hardening howto), the filter is empty, and WinBox responds to anyone who reaches it.
What does the factory firewall look like?
It is an “accept what is needed and drop the rest” firewall towards the router, and a “let out, don’t let in” firewall towards the LAN. On a routerboard with the factory configuration, you find commented rules defconf, in this order (chain and comment):
input defconf: accept established,related,untracked
input defconf: drop invalid
input defconf: accept ICMP
input defconf: accept to local loopback (for CAPsMAN)
input defconf: drop all not coming from LAN
forward defconf: accept in ipsec policy
forward defconf: accept out ipsec policy
forward defconf: fasttrack
forward defconf: accept established,related, untracked
forward defconf: drop invalid
forward defconf: drop all from WAN not DSTNATed
The scheme is the same in both chains: first, responses to established connections, then safe drops and exceptions, and finally the drop. The CHR starts without a factory configuration, and therefore without a firewall.
The practical consequence is just one: every rule you add for traffic from the internet must be placed above drop all not coming from LAN. Below that, the packet has already been dropped.
How do I place a rule in the right spot?
Use place-before while creating it, or move afterwards. add places the new rule at the bottom of the list, i.e., below the drop: this is the most common mistake.
/ip firewall filter
add chain=input protocol=tcp dst-port=22 src-address=10.6.0.50 action=accept \
comment="SSH dal PC del lab" \
place-before=[find comment="defconf: drop all not coming from LAN"]
⚠️ Warning: 10.6.0.50 is my lab PC, on the WAN 10.6.0.0/12 with gateway 10.0.0.1. Use the address of your workstation; if you have doubts about the network, check it with the IP calculator.
To move an existing rule, move takes the rule to be moved and the rule before which to place it:
/ip firewall filter
move [find comment="SSH dal PC del lab"] [find comment="defconf: drop all not coming from LAN"]
Refer to rules by find and a comment, not by number. The numbers are assigned by the last print of the session: in a pasted command, without that print, the number may not exist or may refer to a different rule. In WinBox and WebFig, you change the order by dragging the row in the Filter list.
What are rules with a D?
They are dynamic rules: RouterOS creates them automatically; you did not write them, and they are not saved with the configuration. You see them with the letter D in the flags, mixed with your own in the print; with print where dynamic you see only those.
The most famous one is the first line of the filter when there is a fasttrack rule: special dummy rule to show fasttrack counters. It does not filter anything. It shows how many packets are passing through the fasttrack fast lane, which bypasses the rest of the firewall. You cannot move it: a move … 0 ends with cannot move builtin, as we saw in the DoH howto. However, add … place-before=0 works: in the lab, the new rule ended up at the top, and the dummy moved to second place. And if you remove the fasttrack rule, the dummy stays there until reboot: it does nothing, so do not worry. Other services, such as Kid Control, add them.
How do I know which rule is hitting a packet?
From the counters. Each rule counts the bytes and packets that match it:
/ip firewall filter print stats
/ip firewall filter reset-counters-all
The method is this: you reset the counters, repeat the test (the ping, the WinBox connection, the page that does not open), and print again. The rule whose counter increased is the one that made the decision. If the final drop increases and your accept does not, your accept is below, or it does not match what you think.
Why do we start with established and related?
Most packets belong to connections that have already been accepted, so it is best to let them exit the chain at the first rule. Connection tracking remembers every connection and assigns a state to each packet: new opens a connection, established belongs to an already seen connection, related is related to an existing connection (an ICMP error, FTP data), invalid has no clear state and must be dropped, untracked bypassed tracking due to a RAW rule.
Thus, the rules that decide who gets in only work on new packets. The connection table deserves a separate article.
What are the typical errors?
The rule added at the bottom
You create the accept for the management port with a simple add, the rule ends up below the final drop, and its counter stays at zero forever. It is not broken: no one reaches it. The fix is a move or, better, the place-before from the start.
The overly broad accept at the top
An accept in-interface-list=LAN in forward, placed above everything, lets through what you wanted to block further down: hotspot guests accessing the NAS, PCs accessing encrypted DNS servers. Specific blocking rules must be placed above generic accepting rules.
Locking yourself out
A drop in input written before the accept for your workstation, and the WinBox session dies. Before touching the input chain remotely, enable Safe Mode (the Safe Mode button in WinBox, Ctrl+X from the terminal): if the connection drops, the router automatically reverts the changes.
For all rule parameters, see the MikroTik manual: Filter.
Where do I use this?
- Basic hardening of a MikroTik router: the minimum firewall, starting from scratch;
- Port knocking on MikroTik: rules that log and continue, all above the drop;
- MikroTik Hotspot: how to protect the LAN from WiFi clients: a rule in
forwardplaced above the accepts; - Blocking DNS over HTTPS and DNS over TLS: rules placed before fasttrack with
place-before.
Tested in the lab on a routerboard with RouterOS 7.24.5 (stable): rule order defconf read from /system default-configuration print; log, add-src-to-address-list and passthrough that continue (counters); accept rule added below a useless drop, then moved with move and find (the port opens); place-before; fasttrack dummy rule with cannot move builtin and place-before=0; reset-counters-all.
Frequently asked questions
What is the difference between the input chain and the forward chain?
The input chain sees packets destined for the router itself, for example WinBox or SSH; the forward chain sees packets passing through the router, for example a LAN PC browsing the web.
What does RouterOS do if no firewall rule matches?
It accepts the packet. The default policy is accept: to close a chain, you need an explicit drop rule at the bottom.
Why is my accept rule not working?
Almost always because it is below a drop rule: rules are read from top to bottom and the first match decides. Check with /ip firewall filter print stats which counter is increasing and move the rule with move or recreate it with place-before.
Does the log rule block the packet?
No. log, passthrough and the actions that add to an address list log or annotate and then pass the packet to the next rule. They close the chain accept, drop, reject and tarpit.
What is the dynamic rule at the top with the fasttrack counters?
It is a dummy rule that shows how much traffic passes through fasttrack. It does not filter anything and cannot be moved.



