Skip to content
This page is a translation of the authoritative Chinese source and may lag behind.View the original

Configure Header ACL access rules

Access rules perform admission control based on HTTP request headers, with three scopes: global, key group, and access key. Rules are matched in ascending sort_order; the first match decides allow/deny.

Entry point: Console → Access KeysAccess Rules tab.

Note: access rules apply to both the inference routes (/v1/chat/completions, /v1/messages, etc.) and the model list routes (/v1/models, /v1beta/models). When a header pattern is denied, model-list queries are also blocked. Operators must be aware of this behavior to avoid misconfiguration that prevents clients from fetching the model list.

Rule fields

FieldDescription
Rule NameUnique identifier for the rule, globally unique. Use a scope prefix (e.g. key:mykey__rule1) to avoid naming collisions. Cannot be changed after creation.
OrderSort weight, matched in ascending order; smaller numbers are evaluated first. Rules with the same order are evaluated in scope order Global → Key Group → Access Key (i.e. under the same order a global rule matches first); therefore a key-level or group-level rule that wants to override a same-order global rule must use a smaller order.
Header NameThe HTTP request header name to match (e.g. User-Agent, X-Forwarded-For). Case-insensitive. Leave blank when Match Type is any (that type does not check a request header).
Match TypeThe match method, see the table below.
Match ValueThe value to match. Not needed for the exists / absent / any types.
Actionallow or deny (deny returns 403).
ScopeThe scope: Global (all requests), Key (one or more access keys), Group (one or more key groups). Key and Group are mutually exclusive, and both support multi-select.
Scope valueWhen Scope is Key or Group, select the target key or key group from the dropdown.
EnabledThe rule's on/off switch. A disabled rule is ignored at load time and does not affect other rules.

The table above shows console UI labels; the configuration examples below use the underlying field names, which map as: Header Name = condition_key, Match Type = condition_type, Match Value = condition_value, Action = action, Scope = scope, Order = sort_order.

Match types

TypeDescriptionExample
exactThe header value exactly equals Match ValueUser-Agent = BadBot/1.0
prefixThe header value starts with Match ValueUser-Agent starts with python-requests/
regexThe header value matches a regular expressionUser-Agent matches `(?i)curl
existsThe header is present (regardless of value)Check whether X-Custom-Header is present
absentThe header is absentCheck whether Authorization is missing
anyThe condition is always true; no header is checked, and the rule only takes effect via its Scope"Allow/deny all" for a key: Match Type=any + Scope=Key, Header Name left blank

See Header ACL rule fields for the full reference on field semantics, match types, regex matching semantics, and evaluation logic.

Common usages

  • Block crawlers: condition_key = User-Agent, condition_type = regex, condition_value = (?i)scrapy|crawl|spider, action = deny, scope = Global.
  • Restrict a specific IP range: condition_key = X-Forwarded-For, condition_type = prefix, condition_value = 192.168., action = deny, scope = Global. ⚠️ X-Forwarded-For is a client-forgeable header — it is only reliable when there is a trusted reverse proxy in front of the gateway that overwrites it; otherwise a malicious client can fill in that header arbitrarily to bypass the restriction. For real IP admission control, prefer the real source IP seen directly by the gateway (with TRUSTED_PROXIES) over the forgeable X-Forwarded-For.
  • Only allow requests with a custom header: condition_key = X-Api-Token, condition_type = absent, action = deny, scope = Global (deny when the header is missing).
  • Restrict a specific key group: set scope = Group, select the target key group; the rule only affects keys within that group.
  • Allow/deny all for a key (no header condition): set Match Type = any, Scope = Key and select the target key, leave Header Name blank — the rule applies to all requests from that key. To use it to override a global deny, give it a smaller order than the global rule (under the same order, global wins).

Verifying that a rule takes effect

After configuring a rule, verify with paired curl requests. Using the "block crawlers" rule above (User-Agent containing scrapy → deny) as an example:

bash
# Control group: normal UA, expected to pass (200)
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:7890/v1/chat/completions \
  -H "Authorization: Bearer <your access key>" \
  -H "User-Agent: my-app/1.0" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}'

# Test group: UA matches, expected to be blocked (403)
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:7890/v1/chat/completions \
  -H "Authorization: Bearer <your access key>" \
  -H "User-Agent: scrapy/2.11" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"hi"}]}'

Returning 200 and 403 respectively means the rule works. If the test group still returns 200, check: ① whether the rule's Enabled is on; ② whether it is matched first by an allow rule with a smaller order; ③ whether the scope covers this key.

Complex configuration examples

The following examples demonstrate how to combine regex with first-match-wins semantics to build more complex policies. Fields are given as key = value lines; smaller order is evaluated first.

Example 1: one rule expressing "contains A or contains B or starts with C"

text
condition_key   = User-Agent
condition_type  = regex
condition_value = aaaa|bbb|^aaa
action          = deny
scope           = Global
  • The aaaa branch: value contains aaaa (un-anchored = contains).
  • The bbb branch: value contains bbb.
  • The ^aaa branch: value starts with aaa (the leading anchor applies only to this branch).
  • The three branches are joined with |; matching any one of them denies. Equivalent to merging three rules into one.

Example 2: case-insensitive multi-keyword containment

text
condition_key   = User-Agent
condition_type  = regex
condition_value = (?i:scrapy|crawl|spider|bot)
action          = deny
scope           = Global

(?i:...) makes the alternation inside the parentheses case-insensitive, so Scrapy, CRAWL, and Spider all match. Note that (?i) only affects the range it wraps, and does not affect the rest of the rule.

Example 3: multi-value exact matching (a case exact cannot handle)

exact can only compare against a single value at a time. To express "the value is exactly one of v1, v2, v3", use an alternation with leading and trailing anchors:

text
condition_key   = X-Api-Version
condition_type  = regex
condition_value = ^(v1|v2|v3)$
action          = allow
scope           = Global

^...$ tightens the search semantics to a full match: v1, v2, v3 match; v10, xv1x do not.

Example 4: use contains semantics to block values containing a specific substring

The engine has no standalone contains type, but an un-anchored regex is itself a contains. To block "values of the X-Debug header that contain the substring debug":

text
condition_key   = X-Debug
condition_type  = regex
condition_value = debug
action          = deny
scope           = Global

For example debug matches debug, my-debug-flag, but does not match DEBUG (value matching is case-sensitive); write (?i)debug if you need case-insensitivity.

Example 5: allowlist mode (default_action deny as fallback + a single allow)

Requirement: a key group should only allow clients with a trusted prefix, denying everything else. Use "fall back to default_action when no rule matches":

  1. Set the key group's (or global) acl_default_action to Deny.
  2. Add one allow rule:
text
order           = 10
condition_key   = User-Agent
condition_type  = regex
condition_value = ^trusted-
action          = allow
scope           = Group   (select the target key group)

Effect: value starting with trusted- → matches the rule → allow; other values have no matching rule → fall to default_action → deny. There is no need to write a "deny everything" fallback rule.

Example 6: allow takes precedence over deny (order orchestration)

Requirement: deny common crawlers, but allow your own monitoring probe (whose UA also contains bot, which the crawler rule would wrongly hit). With first-match-wins, the allow rule's order must be smaller:

text
# Rule A: allow your own probe first
order           = 10
condition_key   = User-Agent
condition_type  = regex
condition_value = ^health-probe/
action          = allow
scope           = Global

# Rule B: then deny crawlers
order           = 20
condition_key   = User-Agent
condition_type  = regex
condition_value = (?i:scrapy|crawl|spider|bot)
action          = deny
scope           = Global

health-probe/bot-1 is matched by rule A first → allow, rule B is not evaluated; a normal crawler UA does not match A and falls to B → deny. If the two orders are reversed, the probe is blocked by B first.

Example 7: "does not contain a substring" requires reverse orchestration

The regex engine does not support lookahead, so "allow only if the value does not contain internal" cannot be written as a single regex. Reverse-orchestrate it:

  1. acl_default_action = Deny.
  2. Add a rule "allow if it contains internal": condition_type = regex, condition_value = internal, action = allow.

The semantics become "contains internal → allow; otherwise (contains other values or nothing) → no match → default deny". If the requirement is "deny when it contains internal, allow the rest", set default_action to Allow and add a single internaldeny rule instead.

Evaluation logic

  1. Merge all rules in the current request's scope: global rules + the current key's per-key rules + the current group's per-group rules.
  2. Sort by sort_order ascending (smaller numbers evaluated first; under the same order, the scope order Global → Key Group → Access Key applies), skipping rules with enabled = false.
  3. Evaluate one by one: match by condition_type (any does not read a request header and is always true; the other types take the value of the request header condition_key to match).
  4. The first matching rule decides the result: deny → immediately return 403; allow → pass to the next layer.
  5. When no rule matches, fall back along the default_action chain: per-key acl_default_action → per-group acl_default_action → global Allow.

FAQ

Q: The client gets 403 but the key is correct? Check whether it is blocked by Header ACL. Rules apply to both inference routes and model-list routes; the client may be blocked as early as the first /v1/models call. Temporarily disable the relevant rule or raise its order to troubleshoot.

Q: The rule changed but doesn't take effect? A rule that fails to compile is skipped and a warning is logged. Check whether condition_value is syntactically valid (lookahead / backreferences are not supported). condition_value has a 1024-byte length limit.

Q: How do I let a key group's rule override a global rule? Under the same order, global matches first. To override, use a smaller order, or set the global rule to enabled = false.

Q: How do I "allow/deny unconditionally" for a specific key? Match Type=any + Scope=Key + Header Name left blank. The rule takes effect only via its scope, applying to all requests from that key.

Q: Are model-list calls blocked too? Yes, rules apply to both /v1/models and /v1beta/models. Operators must be aware of this behavior to avoid misconfiguration that prevents clients from fetching the model list.

Next: Header ACL rule fields for the full reference on regex semantics and expressiveness limits; Access key & key group fields for the key group dimension; Audit & security config for rate limiting and IP blocking.