Half of firewall troubleshooting is people arguing about what the firewall is probably doing. diagnose debug flow ends the argument — it prints, step by step, exactly how the FortiGate handled a specific packet: which policy matched, whether it was allowed or denied, what NAT and route it picked. Here's the whole recipe, start to finish, in about ninety seconds.

FortiGate packet path as traced by debug flow allow policy 0 · deny INGRESS recv packet SESSION new · match POLICY forward check ROUTE / NAT route · SNAT EGRESS out iface DROP implicit deny
FIG.01 / The packet's path through the FortiGate — debug flow narrates each step and names where it drops

Set a filter — always

Never run flow debug wide open on a busy box; you'll drown. Filter to the one conversation you care about:

diagnose debug flow filter clear
diagnose debug flow filter addr 10.1.0.5
diagnose debug flow filter proto 6
diagnose debug flow filter port 443
No filter + a busy firewall = a console you can't read and CPU you didn't want to spend. Set addr/port/proto first, every time.

Turn on the trace

Show function names (they tell you where in the stack a decision was made), then start a bounded trace so it stops on its own:

diagnose debug flow show function-name enable
diagnose debug flow trace start 20
diagnose debug enable

Now generate the traffic — a real request, or from a host: curl https://8.8.8.8. The FortiGate narrates what it does.

Read the verdict

An allowed session looks like this:

msg="vd-root:0 received a packet from 10.1.0.5:51420 -> 8.8.8.8:443"
msg="allocate a new session-0004a1c2"
msg="find a route: flag=... gw-203.0.113.1 via wan1"
msg="Allowed by Policy-3: SNAT"

A denied one tells you precisely where it died:

msg="vd-root:0 received a packet from 10.1.0.5:51420 -> 8.8.8.8:443"
msg="iprope_in_check() check failed on policy 0, drop"
msg="Denied by forward policy check (policy 0)"

policy 0 is the tell: no policy matched, so the implicit deny caught it. Now you know it's a policy problem, not routing.

The output names the exact reason — Allowed by Policy-3, Denied by forward policy check, reverse path check fail — so "the firewall is blocking it" becomes "policy 0, implicit deny, no rule matches src 10.1.0.5 → 443." That's a fix, not a theory.

Common verdicts, decoded

  • Denied by forward policy check (policy 0) — nothing matched; check your policy order and src/dst/service.
  • reverse path check fail, drop — asymmetric routing or RPF; the return path doesn't line up. Look at your routes.
  • no new session, drop — usually session-table or state issue; the packet isn't a valid part of an existing flow.
  • enter IPsec interface / matched tunnel — it's going into a VPN; the problem may be phase 2 selectors, not policy.

Clean up

Flow debug is chatty — turn it off when you're done so the next person (or the CPU) isn't stuck with it:

diagnose debug flow trace stop
diagnose debug disable
diagnose debug reset
debug flow answers a different question than the logs. Logs tell you that a packet was dropped; flow tells you why, in the firewall's own words. When they disagree, believe the flow.

Ninety seconds, one filter, one trace — and you stop guessing what the firewall thinks and start reading it.