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.
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. Setaddr/port/protofirst, 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.