A real-life situation
FGT1 has a policy that lets the LAN reach the Internet, and above it a new policy meant to block one website. Users complain the site still opens. Another administrator added the block policy below the general one by mistake. Because the FortiGate stops at the first match, the block never runs. Order is everything.
What a policy matches
| Field | Example | Notes |
|---|---|---|
| Incoming interface | port2 (LAN) | Where the first packet arrives |
| Outgoing interface | port1 (WAN) | Chosen by the routing table, so routing must agree |
| Source | LAN-subnet, or a user group | Addresses, and optionally users or devices |
| Destination | all, a server, an Internet Service | After destination NAT: the real server address |
| Schedule | always, work-hours | When the policy is active |
| Service | HTTP, HTTPS, DNS, ALL | Protocol and destination port |
| Action | ACCEPT or DENY | Plus NAT, logging and security profiles on ACCEPT |
A policy matches only when every field matches. A connection from the LAN to the DMZ doesn't match a LAN-to-WAN policy, however open its other fields are.
Top-down, first match
Here is FGT1's rule base for traffic from port2 (LAN) to port1 (WAN), in sequence order:
| Seq. | ID | Name | Source | Destination | Service | Action |
|---|---|---|---|---|---|---|
| 1 | 4 | Block-Social | LAN-subnet | Social-sites (FQDN group) | ALL | DENY |
| 2 | 2 | Admins-SSH-out | Admin-PCs | all | SSH | ACCEPT |
| 3 | 1 | LAN-to-Internet | LAN-subnet | all | HTTP, HTTPS, DNS | ACCEPT |
| – | 0 | Implicit deny | all | all | ALL | DENY |
Predict the result for each connection before opening the answer.
PC1 (10.0.1.10) opens https://www.example.com
Policy 4 doesn't match (example.com isn't in Social-sites); policy 2 doesn't match (PC1 isn't an admin PC); policy 1 matches. Accepted by ID 1.
PC1 opens a social-media site over HTTPS
Policy 4 matches first. Denied by ID 4, even though policy 1 below would have allowed it.
PC1 (not an admin PC) connects to a server with SSH
Not policy 4, not policy 2 (wrong source), not policy 1 (SSH isn't in its services). Dropped by the implicit deny, policy 0.
Policy ID versus sequence
Every policy gets an ID when it is created, and keeps it. Its sequence is its current position in the list, which is what decides matching. In the table above, ID 4 was created last but moved to the top. Logs, the session table and debug output all name the ID, so learn to read both.
Sessions remember the decision
Only the first packet of a connection goes through the policy lookup. If it is accepted, the FortiGate creates a session recording the addresses, ports, interfaces, policy ID and NAT. Every later packet in either direction matches the session and is forwarded without a new lookup, which is why no policy is needed for replies. When the rule base changes, FortiOS by default marks existing sessions to be checked again, so a new deny policy also affects connections already open.
Why it works this way
First match makes the rule base predictable: you can read it top-down and know the answer. It also means specific rules (exceptions, blocks, narrow allows) must sit above general ones. The implicit deny makes the firewall safe by default: anything you forgot to allow stays blocked.
Common mistakes
- Putting a specific block or allow below a broader policy that already matches the same traffic.
- Forgetting that the outgoing interface comes from the routing table: no route, no policy match.
- Writing policies for return traffic. Sessions handle replies.
- Reading the ID column as the order. The order is the list position.
Key takeaways
- A policy matches on interfaces, source, destination, schedule and service; all must match.
- Policies are checked top-down and the first match wins; put specific policies above general ones.
- Unmatched traffic hits the implicit deny, policy 0, which doesn't log by default.
- IDs are permanent names; the sequence decides matching. Sessions carry the decision for replies.
Check yourself
A DENY policy for a site is at sequence 5. An ACCEPT policy for all LAN web traffic is at sequence 2. Is the site blocked?
Traffic matches no policy. What happens, and is it logged by default?
You move policy ID 3 to the top of the list. What does the traffic log show for sessions it matches afterwards?