The Bengaluru gate-pass analogy
Picture the entry gate of an IT park in Bengaluru — Tech-Park-X with offices from Infosys, TCS, Wipro, and a few startups. The security guard has a printed list of rules:
- "TCS badge holders → allow to TCS tower"
- "Infosys badge holders → allow to Infosys tower"
- "Visitors with prior appointment → allow with escort"
- "Cab drivers → allow only to drop-off zone"
- "Everyone else → deny"
The guard reads top to bottom. The first rule that matches stops the lookup. If an Infosys employee shows up with a TCS-visitor pass at the top of the list by mistake, rule 1 fires first and they get routed wrong. Rule order is the rule.
That's exactly how a PAN-OS security policy works. Top-down, first match wins, no further rules evaluated. Get the order wrong and you've either opened a hole (overly broad rule on top) or you've shadowed a specific rule (the broad rule fired before the specific one ever got a chance).
Three rule types, two predefined defaults
Before any rule you write fires, two predefined rules already exist at the bottom of every PAN-OS rulebase — they're invisible until you go to Policies → Security and look for the grey rows at the bottom:
| Rule | Scope | Action | Logging |
|---|---|---|---|
intrazone-default | Same source & destination zone | Allow | Off by default |
interzone-default | Different source & destination zones | Deny | Off by default |
This means: traffic inside a zone is allowed by default. Traffic between zones is denied by default unless your custom rules let it through. Both defaults are silent — no traffic log row appears for either default action unless you override them and turn on logging.
The three rule types when YOU author a rule:
- universal (default) — matches both intra- and inter-zone traffic whose zones you've specified
- intrazone — matches only traffic where source zone == destination zone (any zones you list)
- interzone — matches only when source zone ≠ destination zone
For 99% of your rules, leave the type at universal and let the zone lists do the work. The other two are for niche cases.
Top-down evaluation — drawn out
📍 Scenario · Aditya, junior engineer at Wipro Hyderabad
Aditya gets a ticket: "block all SQL traffic to the DMZ MySQL server 10.50.5.10". He adds a new rule at position 12 in the rulebase: src=trust, dst=dmz, app=mysql, action=deny. He commits. Six hours later, traffic still flows. He runs test security-policy-match from the CLI — turns out rule 6 (universal allow trust→dmz on app-default) fires first. His new rule is shadowed. Fix: move the new deny rule above rule 6, OR scope rule 6 tighter. Lesson: rule order is the rule. Position 12 is below position 6 — that's all the firewall cares about.
The application-default trap — the L1-to-L2 gateway question
The Service column of every PAN-OS rule has three common values, and picking the wrong one is the single most common L1 mistake:
| Service setting | What it does | When to use |
|---|---|---|
application-default | Allows the app only on its IANA-standard port. ssh = 22, web-browsing = 80, ssl = 443, mysql = 3306, etc. | Always — unless you specifically need a non-standard port |
service-http / service-https | Predefined service objects for port 80 / 443 over TCP | When you want to allow the app on a specific port that differs from default |
any | Allows the app on any port | Almost never — opens evasion paths. Use only after thorough analysis. |
From the official PAN-OS best-practices guide: "Always use application-default unless you are using a more restrictive list of ports than the standard ports for an application."
Why this matters: a rule with application-default means SSH (App-ID ssh) is allowed on TCP 22 — and only on TCP 22. If an attacker tunnels SSH over port 443 to evade your perimeter, that flow won't match this rule. A rule with Service = any would allow the same SSH session on port 443 — which is exactly the evasion the attacker wanted.
When your business legitimately runs an app on a non-standard port — say, an in-house web app on TCP 8443 — write the rule with App-ID = web-browsing + Service = a custom service object pointing at TCP 8443. Don't fall back to App = any + Service = tcp-8443; that opens any application on that port. The whole point of NGFW is App-ID. Use it.
Application shift — the surprise that breaks SaaS rules
App-ID doesn't get a final verdict from the first packet. PAN-OS lets the first few packets through tagged as the closest known app, then re-evaluates as more bytes arrive. This is called application shift.
A YouTube session starts as App-ID = ssl, then shifts to web-browsing after HTTP CONNECT, then shifts to youtube-base as soon as PAN sees a recognizable signature in the SSL handshake. Each shift triggers a fresh policy lookup against the new App-ID.
Practical consequence: a rule that allows SSL but denies youtube — written with the deny rule LOWER in the rulebase than the allow rule — will let the connection start (matching SSL allow) and then drop it (when shift to youtube triggers a re-lookup that hits the deny). User experience: page partially loads, then dies. Very confusing to debug.
Fix: when you write App-ID-specific deny rules, place them above the broader category allows. The deny for youtube goes above the allow for ssl.
Implicit application dependencies
Many App-IDs require other App-IDs to be allowed first. youtube-base needs ssl + web-browsing. facebook-base needs web-browsing. ms-update needs ssl. PAN-OS calls these implicit dependencies.
For some apps (HTTP-, SSL-, MS-RPC-, RTSP-based) the firewall handles dependencies automatically — you don't need to explicitly allow web-browsing for a youtube rule to work. For other apps, the GUI shows a warning "Application Dependency" when you save the rule and you must allow the dependencies in their own rule.
From PAN-OS docs: "Applications for which the firewall cannot determine dependent applications on time will require that you explicitly allow the dependent applications when defining your policies."
🧪 Interactive — the in-page policy tester
🧪 Try it — pick a source / dest / app and watch which rule fires
This is a small replica of test security-policy-match. The rulebase below has 6 rules — set the request fields, click Match, and see which rule fires (with reasoning) and which rules below it are shadowed.
Rulebase (in evaluation order)
trust-lan → dmz, mysql, port 3306 (matches rule 3). Now try the same with port 4406 — rule 3 won't match (app-default mismatch). Try guest-vlan → untrust-wan, ssh, port 22 — rule 1 fires first and denies SSH even from guest. That's the power of placing global denies at the top.Shadow rules and the Policy Optimizer
A shadow rule is a rule that can never fire because a broader rule above it always matches the same traffic first. PAN-OS catches the obvious cases at commit time and shows a "Rule shadow" warning. The CLI command show shadow-warning dumps the full list.
Beyond simple shadowing, PAN-OS 10.x+ ships with the Policy Optimizer — Policies → Security → Policy Optimizer. It surfaces:
- Unused rules — never matched a packet since deployment / reset
- Unused apps in rules — apps you allowed but no traffic uses them
- Port-based rules — old service-port rules that can be converted to app-based with App-ID
Treat this as your quarterly audit tool. A rulebase that grows for 5 years without optimization is 30–60% dead weight. Per the Policy Optimizer docs: "The first real optimization step is a clean audit, starting by inventorying every rule and categorizing it as active, stale, duplicate, shadowed, or overly permissive."
The test security-policy-match command — your daily debug tool
The single best CLI command in PAN-OS for "is the rule firing?" arguments is test security-policy-match. It simulates a packet's path through the rulebase and tells you exactly which rule would match. No actual traffic required.
Run from the PAN-OS CLI in operational mode. The firewall computes the match using the live rulebase as if a real packet arrived.
test security-policy-match from trust-lan to dmz source 192.168.10.50 destination 10.50.5.10 protocol 6 destination-port 22 application ssh
"Block-SSH-Outbound" {
from any;
to any;
source any;
destination any;
application [ ssh ];
action deny;
log-end yes;
}
The rule called "Block-SSH-Outbound" sits at the top of the rulebase. Even though the operator wanted to test trust→dmz SSH (which the operator might have expected to hit an "Allow-DB-Admin-SSH" rule lower down), the broad block-all-SSH rule at the top fires first. Diagnosis complete in 5 seconds.
show shadow-warning
shadow warning for rule "Block-MySQL-DMZ" (id 12):
shadowed by rule "Allow-Trust-to-DMZ-All" (id 6)
reason: rule 6 matches a superset of rule 12's source/destination/application
shadow warning for rule "Allow-Specific-Vendor-API" (id 24):
shadowed by rule "Allow-All-Web-Outbound" (id 18)
reason: rule 18 matches a superset of rule 24's source/destination/application
Total shadow warnings: 2
show rule-hit-count vsys vsys1 rule-base security rules all
rule name hits first-hit last-hit Allow-Trust-to-Internet-Web 2,847,193 2025-09-02 09:14:22 2026-05-24 18:30:01 Allow-AD-DCs 412,508 2025-09-02 10:01:08 2026-05-24 18:29:55 Allow-DNS-Outbound 388,221 2025-09-02 09:14:38 2026-05-24 18:30:01 Allow-O365-Saas 201,902 2025-09-02 11:42:11 2026-05-24 18:29:58 Block-SSH-Outbound 12,338 2025-09-15 02:13:09 2026-05-24 17:58:04 Allow-Backup-Server 0 -- -- Old-Legacy-Rule-from-2018 0 -- --
Two rules with zero hits in 9 months — strong candidates for removal. Always investigate before deleting (a rule may be for an annual maintenance window) but a long zero-count is the right starting point for audit.
Common mistakes that cost an interview point
any on a production rule"Just to be safe" is the dangerous justification. Service = any lets the named App-ID run on any TCP/UDP port — which is exactly the evasion an attacker wants. Always set Service = application-default unless you have a documented business reason to widen it. PAN-OS docs are unambiguous on this.
"Block this user from this app" rule placed at the end of the rulebase. It will never fire because the broader allow rule above it matched first. Always place specific denies ABOVE broader allows. show shadow-warning catches this at commit; test security-policy-match catches it pre-commit.
Without "log at session end" enabled, the rule produces no Traffic log entry. Two consequences: (1) SIEM is blind to allowed flows, (2) Policy Optimizer can't compute hit counts. Enable logging on every production rule. It's a setting that's free to enable and expensive to discover you don't have at audit time.
An L1 engineer changes a rule's type from universal to intrazone "to make it tighter". Now the rule only matches when source-zone == destination-zone. If the policy intent was inter-zone trust→dmz traffic, the rule silently stops matching. Symptom: traffic suddenly denied with no rule-change visible in the diff. Diagnosis: test security-policy-match shows the rule "not matched" reason. Almost always leave at universal unless you have a specific reason otherwise.
Pro tips from the field
Both intrazone-default and interzone-default have logging off by default. Override them (Policies → Security → bottom of list → Override) and turn on log-at-session-end. Auditors will thank you; incidents will be traceable; Policy Optimizer will have data to recommend cleanup.
PAN-OS supports tags + colour-coding on rules. Tag rules by owner (net-team, app-team, security-team) or by purpose (compliance, temp-debug, vendor-access). The Policies view becomes scannable, audit reports become attributable, and stale debug rules become impossible to hide.
New engineers love writing "allow trust→dmz any" rules with App = any. Resist. Even transit rules between trust zones should specify expected app categories (e.g., business-systems, networking, infrastructure). It costs five extra minutes per rule and gives you 100x better visibility into what's actually crossing the firewall.
📋 Quick reference — print and pin
| Concept | One-liner |
|---|---|
| Evaluation order | Top-down, first match wins. Lookup stops after match. |
| Default rules | intrazone = allow (silent), interzone = deny (silent). Override to log. |
| Rule types | universal (default), intrazone, interzone. Leave at universal 99% of the time. |
| Service field | application-default for 90% of rules. Custom service for known non-default ports. any = security gap. |
| App-ID shift | SSL → web → youtube. Each shift triggers fresh policy lookup. Place specific denies ABOVE broader allows. |
| Dependencies | Some apps (HTTP/SSL/MS-RPC/RTSP) get implicit dependencies. Others need explicit allow. |
| Shadow detection | show shadow-warning CLI + Policy Optimizer GUI. |
| Match testing | test security-policy-match from X to Y source A destination B application Z. |
| Hit count audit | show rule-hit-count vsys vsys1 rule-base security rules all. |
| Audit cadence | Quarterly Policy Optimizer review. Remove stale, narrow over-broad, replace port-based with App-ID. |
📚 Sources cited inline
- Palo Alto Networks Docs — Create a Security Policy Rule (PAN-OS 11.1). docs.paloaltonetworks.com
- Palo Alto Networks Docs — Safely Enable Applications on Default Ports (application-default). docs.paloaltonetworks.com
- Palo Alto Networks Docs — Security Policy Rulebase Best Practices. docs.paloaltonetworks.com/best-practices/security-policy-best-practices
- Palo Alto Networks Docs — Resolve Application Dependencies / Applications with Implicit Support.
- LIVECommunity — Tips & Tricks: Test Policy Match. live.paloaltonetworks.com/t5/community-blogs/ba-p/445394
- LIVECommunity — Shadow Rule Warnings (multiple threads: 273444, 206008, 38567).
- Palo Alto Knowledge Base — How to Check Which Security Rule is Used the Most + Identify Unused Policies.
📝 Check your understanding
10 scenario questions in PCNSE/PCNSA format. Pick one answer per question. You need 70% (7 of 10) to mark this lesson complete on your profile.
What's next?
You now know how PAN-OS finds the right rule. Next we tackle what happens BEFORE the rule lookup — Network Address Translation. Source NAT, Destination NAT, U-turn / hairpin, dynamic-IP-and-port (DIPP), and the order-of-operations gotcha that makes "NAT not matching" the most common Day-1 ticket.