# Actions and Default Deny in XC Service Policies

> A rule's action is ALLOW, DENY, or NEXT_POLICY. Beyond the verdict, a rule can attach modifiers like WAF, bot defense, or rate limiting that fire on a match. And the whole system is deny-by-default: a request that matches nothing is denied. Knowing these makes a policy's real effect legible.

Source: https://ronutz.com/en/learn/xc-service-policy-actions-and-default-deny  
Updated: 2026-07-01  
Related tools: https://ronutz.com/en/tools/f5xc-service-policy-explainer

---

Once you know how a service policy matches a request, the remaining question is what happens on a match. That is the action, and it has a bit more nuance than allow-or-block.

## The three actions

A rule's `action` is one of three values. **ALLOW** lets the request proceed. **DENY** terminates processing and returns an error to the client. **NEXT_POLICY** stops evaluating the current policy and moves to the next policy in the set, skipping any remaining rules in the current one. (The broader policy framework also documents actions like NEXT_POLICY_SET, LAST_POLICY, and GOTO_POLICY for chaining across policy sets, but the inline rule action is one of the three above, defaulting to DENY.)

The default is worth noting: if a rule does not state an action, it denies. Deny is the safe default at every level of this system.

## Default deny

The most important behavior is not written on any single rule. If a request does not match any rule that allows it, it is denied. A service policy is fundamentally a list of reasons to allow (or to explicitly deny) a request; absence of a match means no.

This is why an empty `rule_list` denies everything, and why a `rule_list` that only contains DENY rules, with nothing to allow the rest, also denies everything that is not explicitly caught. When you audit a policy, the question is always: what, if anything, actually allows the traffic you expect to serve?

## Whole-policy shortcuts

Not every policy uses a rule list. The rule choice can also be a blunt instrument: `allow_all_requests` permits everything that reaches the policy, and `deny_all_requests` blocks everything. These are legitimate but worth flagging, because an `allow_all_requests` policy performs no checks at all, and a stray one in a policy set can undo the intent of everything around it.

The `allow_list` and `deny_list` forms are source-based shortcuts. They match on where a request comes from (IP prefixes, prefix sets, ASNs, countries, TLS fingerprints) and carry a default action for the non-matching case. An allow_list allows matching sources and applies its default to the rest; a deny_list denies matching sources.

## Modifiers: more than a verdict

A rule does more than allow or deny. On a match it can attach **modifiers** that shape how the request is handled: a `waf_action` (skip or customize App Firewall processing), a `bot_action`, rate limiters, an OpenAPI validation action, response masking, and others. These are not the verdict; they ride along with it. A rule can also carry an `expiration_timestamp`, after which it still exists in the config but is no longer applied, and a `log_rule_evaluation` flag that logs matches without changing the decision.

The explainer separates these clearly: it shows each rule's verdict as an allow, deny, or next-policy badge, lists the predicates that must match, and notes the modifiers that fire on a match, so the difference between "this rule decides the request" and "this rule also does something on the way" is never blurred.
