Selectors
A selector determines which requests a policy applies to. It is defined per-policy inside spec.selector.
Fields
{
"selector": {
"hosts": ["api.example.com"],
"pathPrefix": "/api/v1/",
"methods": ["GET", "POST"]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
hosts |
array of strings | no | Optional host filter. Exact match against request host (case-insensitive). If absent, all hosts match. |
pathPrefix |
string | conditional | Prefix match. Must start with /. |
pathExact |
string | conditional | Exact path match. Must start with /. |
methods |
array of strings | no | HTTP method filter. Case-sensitive. If absent, all methods match. |
At least one of pathPrefix or pathExact must be provided.
Host matching rules
hosts
hosts scopes a selector to one or more domain names.
{ "hosts": ["api.example.com", "admin.example.com"], "pathPrefix": "/" }
Rules:
- Matching is exact by hostname (no wildcard syntax in v1).
- Matching is case-insensitive.
- Request host is normalized before compare (
API.EXAMPLE.COMmatchesapi.example.com). - If
hostsis omitted, selector is host-agnostic and matches any host.
Use hosts when one edge instance serves multiple domains and you need per-domain policy isolation.
Path matching rules
pathPrefix
Matches any path that begins with the given prefix.
The prefix must end with / for correct sub-path matching (the engine normalises a missing trailing slash). Exception: the root selector "pathPrefix": "/" matches all paths.
{ "pathPrefix": "/api/v1/" }
Matches:
/api/v1/users/api/v1/users/123/api/v1/
Does not match:
/api/v2/users/api/v10/
pathExact
Matches only the exact path string.
{ "pathExact": "/v1/chat/completions" }
Matches:
/v1/chat/completions
Does not match:
/v1/chat/completions/stream/v1/chat/
Both in one selector
A policy can specify both pathPrefix and pathExact. The route index builds separate trie entries for each, and the policy is matched if either applies.
{
"selector": {
"hosts": ["api.example.com"],
"pathPrefix": "/api/",
"pathExact": "/health"
}
}
Method filtering
{
"selector": {
"hosts": ["api.example.com"],
"pathPrefix": "/api/",
"methods": ["POST", "PUT", "PATCH"]
}
}
Only requests whose HTTP method is in the methods list will match. The comparison is case-sensitive; use uppercase.
Supported values: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT, TRACE.
If methods is absent or empty, the selector matches any method.
Multiple policies on the same route
Multiple policies can match the same request. All matched policies are evaluated in the order they appear in bundle.policies. A request is only allowed if all matching policies allow it.
{
"policies": [
{
"id": "global-limit",
"spec": {
"selector": { "pathPrefix": "/" },
"rules": [{ "name": "global", "limit_keys": ["ip:address"], ... }]
}
},
{
"id": "api-limit",
"spec": {
"selector": {
"hosts": ["api.example.com"],
"pathPrefix": "/api/",
"methods": ["POST"]
},
"rules": [{ "name": "api-post", "limit_keys": ["header:x-api-key"], ... }]
}
}
]
}
A POST https://api.example.com/api/users request will be evaluated against both policies.
Route index internals
Selectors are compiled at bundle load time. Matching first filters by host, then performs trie lookup by path segments:
host=api.example.com
root
├── api
│ └── v1 ← prefix policies: [api-v1]
│ └── users ← exact policies: [users-exact]
└── health ← exact policies: [health-check]
Lookup is O(depth) for path traversal within the selected host bucket.