> ## Documentation Index
> Fetch the complete documentation index at: https://verifyngo.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Traffic rules: allow, challenge, or deny incoming requests

> Define rules in config.json, a plain-text file, or a YAML policy to allow, challenge, or deny traffic based on path, user agent, or IP address.

verifyngo evaluates a list of rules for every incoming request. The first rule that matches decides the action. If no rule matches, `default_action` applies (defaults to `challenge`). Rules can match on the URL path, the User-Agent header, or a client IP address or CIDR range. The three actions are `allow` (pass the request through to the upstream), `challenge` (serve a CAPTCHA), and `deny` (block the request immediately).

## Inline rules (config.json)

You can define rules directly in `config.json` under the `rules` array. Each rule has a `match` object and an `action` string.

```json theme={null}
"rules": [
  { "match": { "path": "^/api/" }, "action": "allow" },
  { "match": { "ua": "python-requests|curl/|Go-http-client" }, "action": "deny" },
  { "match": { "cidr": ["185.220.0.0/16"] }, "action": "deny" },
  { "match": { "path": "^/search" }, "action": "challenge" }
]
```

<ParamField body="match.path" type="string">
  A regular expression matched against the request URL path. The rule fires when the path matches.
</ParamField>

<ParamField body="match.ua" type="string">
  A regular expression matched against the `User-Agent` header. The rule fires when the header matches.
</ParamField>

<ParamField body="match.cidr" type="array of strings">
  A list of IP addresses or CIDR ranges. The rule fires when the client IP falls within any of the listed ranges.
</ParamField>

<ParamField body="action" type="string" required>
  What to do when the rule matches. One of `allow`, `challenge`, or `deny`.
</ParamField>

<Note>
  Rules are first-match-wins and are evaluated from top to bottom. Put more specific rules before broader ones to avoid an early rule shadowing a later one.
</Note>

## Plain-text rules file

For larger rule sets, or when you want to update rules without restarting verifyngo, set `rules_file` in `config.json` to a path and create a `.txt` file there. verifyngo watches the file and picks up any edits within about 10 seconds.

```json theme={null}
"rules_file": "/etc/verifyngo/rules.txt"
```

Each line in the file follows the format `<action>  <type>  <pattern>`. Here is the full example from `rules.txt.example`:

```
allow     path   ^/api/
deny      ua     python-requests|curl/|Go-http-client
deny      cidr   185.220.0.0/16
challenge path   ^/search
```

The three match types are:

* **`path`** — matches the request URL path using the pattern as a regular expression.
* **`ua`** — matches the `User-Agent` header using the pattern as a regular expression.
* **`cidr`** — matches the client IP against a single CIDR range (or a bare IP address).

Lines starting with `#` are treated as comments and skipped. Empty lines are also ignored.

<Tip>
  Use the rules file for rules you change often. You don't need to restart verifyngo — it picks up changes to the file automatically within about 10 seconds.
</Tip>

## YAML policy file

For more advanced matching, set `policy_file` in `config.json` to a YAML file path. The policy file lets you define named network groups and write expressive conditions that can match on path, user agent, HTTP method, IP address, request headers, and more.

```json theme={null}
"policy_file": "/etc/verifyngo/policy.yaml"
```

A policy file has three top-level sections:

* **`networks`** — named groups of IP ranges or ASNs that you can reference in rule conditions.
* **`conditions`** — named, reusable expressions you can reference inside rules.
* **`rules`** — the list of rules to evaluate, each with a `name`, `action`, and one or more conditions.

```yaml theme={null}
networks:
  bad_actors:
    - cidr: 185.220.0.0/16
    - asn: 64496

conditions:
  is_bot_ua:
    - userAgent.matches("python-requests|curl/|Go-http-client")

rules:
  - name: block bad actor networks
    action: deny
    condition: network(ip, "bad_actors")

  - name: deny known bots
    action: deny
    conditions:
      - ($is_bot_ua)

  - name: allow api
    action: allow
    condition: path.startsWith("/api/")
```

Conditions can use the following variables:

* `path` — the request URL path.
* `userAgent` — the `User-Agent` header value.
* `method` — the HTTP method (e.g. `GET`, `POST`).
* `ip` — the client IP address as a string.
* `headers["Header-Name"]` — any request header value.

Available functions in condition expressions:

* `matches(value, "regex")` — true if the value matches the regular expression.
* `contains(value, "substring")` — true if the value contains the substring.
* `startsWith(value, "prefix")` — true if the value starts with the prefix.
* `network(ip, "group_name")` — true if the IP falls within the named network group.

<Note>
  When `policy_file` is set, verifyngo uses the policy file exclusively and ignores both `rules` and `rules_file`. Like the plain-text rules file, the policy file is reloaded automatically within about 10 seconds whenever it changes on disk.
</Note>
