# Edge settings

> Read and change the edge settings of a zone (WAF, origin, TLS, cache, AI agents…) and the effective configuration of a name.

Edge settings control how the edge serves the proxied names of a zone: WAF mode and level, origin and origin TLS, HSTS, cache, access gate, AI agents, RUM and more. Settings apply to every proxied name of the zone, and each name can override them under `hosts`.

| Operation | Endpoint |
|---|---|
| [Get edge settings](https://developers.openwaf.dev/api/edge-settings/#get-zone-edge) | `GET /v1/orgs/{org}/zones/{zone}/edge` |
| [Update edge settings](https://developers.openwaf.dev/api/edge-settings/#update-zone-edge) | `PATCH /v1/orgs/{org}/zones/{zone}/edge` |
| [Get effective configuration of a name](https://developers.openwaf.dev/api/edge-settings/#get-host-effective) | `GET /v1/orgs/{org}/zones/{zone}/hosts/{host}/effective` |

## Get edge settings

```http
GET /v1/orgs/{org}/zones/{zone}/edge
```

Returns the edge settings of the zone (the defaults if they were never saved). The `ETag` header carries the current configuration version.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |

#### Response

- `200`: Returns [ZoneEdgeSettings](https://developers.openwaf.dev/api/schemas/#zone-edge-settings).

| Field | Type | Description |
|---|---|---|
| `zone_id` | string (uuid) | Zone id. |
| `mode` | string | One of `active`, `log`, `off`. Default `active`. |
| `waf_level` | string | One of `off`, `low`, `medium`, `high`. Default `medium`. |
| `rate_limit_rpm` | integer | Basic limit per IP and minute; 0 = off. Default `0`. Minimum 0. |
| `timeout_ms` | integer | Default `0`. Minimum 0. |
| `enable_feed` | boolean | Default `true`. |
| `stack` | string | One of `generic`, `wordpress`, `nextjs`, `static`. Default `generic`. |
| `block_page_mode` | string | One of `nginx`, `branded`, `apache`, `generic`, `custom`. Default `branded`. |
| `fake_server_header` | string | Default `""`. |
| `block_page_html` | string | At most 262144 characters. |
| `access_gate` | object |  |
| `access_gate.enabled` | boolean | Default `false`. |
| `access_gate.path` | string | Default `/`. |
| `access_gate.ttl_hours` | integer | Default `0`. Minimum 0. |
| `access_gate.emails` | array of string | Email addresses allowed through the access gate. |
| `max_body_mb` | integer | Default `0`. Minimum 0. |
| `redirect_allow_hosts` | array of string |  |
| `cors_allow_origins` | array of string |  |
| `hsts` | object |  |
| `hsts.max_age` | integer | Default `0`. From 0 to 63072000. |
| `hsts.include_subdomains` | boolean |  |
| `hsts.preload` | boolean |  |
| `compression` | boolean | Default `false`. |
| `origin` | object | The site's upstream URL = scheme://<record content>[:port] (IPv6 in brackets). |
| `origin.scheme` | string | One of `http`, `https`. Default `https`. |
| `origin.port` | integer | 0 = the scheme's default. Default `0`. From 0 to 65535. |
| `origin.tls_verify` | string | Verification of an https origin's certificate: full encrypts without verifying; full_strict verifies the chain (public roots + OpenWAF Origin CA), dates, name and revocation (failure = 526). New zones: full_strict; zones created before the feature: full. Only affects https origins. One of `full`, `full_strict`. |
| `origin.server_name` | string | SNI and verification name; empty = the origin host (or the site's domain if the origin is an IP). At most 253 characters. |
| `origin.client_cert` | boolean | The edge presents its client certificate to the origin (mTLS). Default `false`. |
| `hosts` | [HostSettingsMap](https://developers.openwaf.dev/api/schemas/#host-settings-map) | Own settings of some names of the zone: each name's site uses the zone's settings merged with its own field by field. `{}` when there are none (always present in answers). In a PATCH it is a JSON merge patch (RFC 7396) per name. |
| `redirects` | array of object | Redirect rules: the edge answers `status` with `Location` = `target` (+ the rest of the path after `path_prefix` if `preserve_path`, + the query string if `preserve_query`) without reaching the origin. The longest prefix of the name wins. `[]` when there are none (always present in answers). In a PATCH the list is replaced. The name needs a proxied record. At most 50 items. |
| `redirects[].host` | string | Full name in the zone (the zone or below it, no wildcard); lowercased. |
| `redirects[].path_prefix` | string | Starts with /; no ?, #, spaces or control characters. Stored in canonical form (non-ASCII and reserved characters as uppercase %XX: /año → /a%C3%B1o). /blog matches /blog and /blog/x, not /blogging. Default `/`. At most 1024 characters. |
| `redirects[].target` | string | Absolute http(s) URL without credentials, fragment or spaces; may have a query string. Stored with a lowercase scheme and non-ASCII path and query characters as %XX; the host in ASCII (punycode). It cannot point to a path that the rule itself captures. At most 2048 characters. |
| `redirects[].preserve_path` | boolean | Default `true`. |
| `redirects[].preserve_query` | boolean | Default `true`. |
| `redirects[].status` | integer | One of `301`, `302`, `307`, `308`. Default `301`. |
| `cache` | [EdgeCache](https://developers.openwaf.dev/api/schemas/#edge-cache) | Response cache at the edge, off by default. In a PATCH it is a JSON merge patch: objects are merged, lists (`rules`, `query.params`) are replaced, `"dev_mode_until": null` turns development mode off and `"cache": null` restores the defaults. |
| `cache.enabled` | boolean | Without rules only static extensions are eligible (never HTML or JSON); the origin's Cache-Control is respected. The WAF always runs first. Default `false`. |
| `cache.dev_mode_until` | string (date-time) or null | Development mode: the cache is not used until then (nothing is deleted); null = off. |
| `cache.query` | [CacheQuery](https://developers.openwaf.dev/api/schemas/#cache-query) |  |
| `cache.browser_ttl` | integer | Minimum browser TTL in seconds (a longer max-age from the origin wins); 0 = respect the origin. Default `0`. From 0 to 31536000. |
| `cache.deception_armor` | boolean | Do not store responses whose Content-Type does not match the extension. Default `true`. |
| `cache.serve_stale` | [CacheServeStale](https://developers.openwaf.dev/api/schemas/#cache-serve-stale) |  |
| `cache.max_object_mb` | integer | Default `100`. From 1 to 512. |
| `cache.rules` | array of [EdgeCacheRule](https://developers.openwaf.dev/api/schemas/#edge-cache-rule) | In order; if several match, the last one wins for each setting. At most 25 items. |
| `early_hints` | [EdgeEarlyHints](https://developers.openwaf.dev/api/schemas/#edge-early-hints) | 103 Early Hints, off by default. In a PATCH it is a JSON merge patch: fields are merged, `extra_origins` is replaced and `"early_hints": null` restores the defaults. |
| `early_hints.enabled` | boolean | Answer 103 to HTML navigations that go to the origin. Default `false`. |
| `early_hints.mode` | string | Where links are learned from: the origin's Link headers, the first 32 KiB of the HTML, or both. One of `origin_links`, `html_head`, `both`. Default `both`. |
| `early_hints.extra_origins` | array of string | Origins besides the site's own whose resources can be announced: `https://host[:port]`, without a path. They are normalized (spaces, lowercase, a trailing `/` and `:443` removed); duplicates or non-https values answer 422. At most 10 items. |
| `ai_agents` | [ZoneEdgeAIAgents](https://developers.openwaf.dev/api/schemas/#zone-edge-ai-agents) |  |
| `rum` | [ZoneEdgeRUM](https://developers.openwaf.dev/api/schemas/#zone-edge-rum) | Real user measurement of Core Web Vitals, off by default. In a PATCH it is a JSON merge patch: the given fields replace the current ones (`exclude_paths` as a whole), a field set to null returns to its default and `"rum": null` restores the default object. It is validated even when off. |
| `rum.enabled` | boolean | The edge injects /__waf/rum.js in the site's HTML pages and receives its beacons at /__waf/rum. Default `false`. |
| `rum.sample_rate` | number | Fraction of page views that get the script, (0, 1]. Default `1`. Maximum 1. |
| `rum.exclude_paths` | array of string | Path prefixes that never get the script (spaces are trimmed; empty entries are dropped). Default `[]`. At most 50 items. Items match `^/[^?#]*$`. |
| `rate_rules` | array of [EdgeRateRule](https://developers.openwaf.dev/api/schemas/#edge-rate-rule) | Per-path rate limits, in evaluation order; none by default. In a PATCH the whole list is replaced (`[]` removes them all). Each name can have its own list in `hosts.<name>.rate_rules`. At most 25 items. |
| `updated_at` | string (date-time) | Last change. |

Response headers: `ETag`.

#### Errors

| Status | Description |
|---|---|
| `404` | Not found, or not visible to the token (`not_found`). |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/edge" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getZoneEdge({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_zone_edge(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  "mode": "active",
  "waf_level": "medium",
  "rate_limit_rpm": 0,
  "timeout_ms": 0,
  "enable_feed": true,
  "stack": "generic",
  "block_page_mode": "branded",
  "fake_server_header": "",
  "block_page_html": "",
  "access_gate": {
    "enabled": false,
    "path": "/checkout",
    "ttl_hours": 0,
    "emails": [
      "ops@example.com"
    ]
  },
  "max_body_mb": 0,
  "redirect_allow_hosts": [
    "www.example.com"
  ],
  "cors_allow_origins": [
    "https://app.example.com"
  ],
  "hsts": {
    "max_age": 0,
    "include_subdomains": false,
    "preload": false
  },
  "compression": false,
  "origin": {
    "scheme": "https",
    "port": 0,
    "tls_verify": "full",
    "server_name": "",
    "client_cert": false
  },
  "hosts": {},
  "redirects": [
    {
      "host": "www.example.com",
      "path_prefix": "/api/",
      "target": "https://www.example.com/new",
      "preserve_path": true,
      "preserve_query": true,
      "status": 301
    }
  ],
  "cache": {
    "enabled": false,
    "dev_mode_until": "2026-10-27T09:30:00Z",
    "query": {
      "mode": "all",
      "sort": false,
      "params": [
        "example"
      ]
    },
    "browser_ttl": 0,
    "deception_armor": true,
    "serve_stale": {
      "while_revalidate": true,
      "on_error_max_s": 86400
    },
    "max_object_mb": 100,
    "rules": [
      {
        "id": "example",
        "hosts": [],
        "match": {},
        "action": "block",
        "edge_ttl": {},
        "browser_ttl": {},
        "query": {},
        "serve_stale": {},
        "vary_bypass_if_present": []
      }
    ]
  },
  "early_hints": {
    "enabled": false,
    "mode": "both",
    "extra_origins": [
      "https://cdn.example.com"
    ]
  },
  "ai_agents": {
    "enabled": false,
    "mode": "log",
    "spoofed_challenge": "log",
    "template": "search_ok_block_training",
    "policy": {
      "ai_training": "allow",
      "ai_search": "allow",
      "ai_user_agent": "allow",
      "search_engine": "allow",
      "unverified_automation": "allow",
      "spoofed": "allow",
      "unverified_bot": "allow",
      "trap_hit": "allow",
      "secret_probe": "allow",
      "robots_violation": "allow",
      "agent_burst": "allow"
    },
    "bot_overrides": [
      {
        "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
        "action": "block"
      }
    ],
    "trap": {
      "enabled": false
    },
    "tarpit": {
      "enabled": false
    },
    "robots": {
      "enabled": false,
      "content_signals": {
        "search": "yes",
        "ai_input": "yes",
        "ai_train": "yes"
      }
    },
    "markdown": {
      "enabled": false
    },
    "block_response": {
      "status": 403,
      "message": ""
    },
    "endpoints": [
      {
        "path_prefix": "/api/",
        "methods": [
          "POST"
        ],
        "max_body_bytes": 0,
        "rpm_per_ip": 0,
        "rpm_per_session": 0,
        "session_header": "X-Session-Id",
        "session_cookie": "session",
        "injection_action": "log",
        "limit_action": "log",
        "kind": "mcp",
        "mcp": {}
      }
    ]
  },
  "rum": {
    "enabled": false,
    "sample_rate": 1,
    "exclude_paths": []
  },
  "rate_rules": [
    {
      "id": "example",
      "name": "example.com",
      "enabled": true,
      "hosts": [
        "www.example.com"
      ],
      "path_prefix": "/auth/login",
      "path_exact": "/login",
      "methods": [
        "POST"
      ],
      "header_present": "X-Api-Key",
      "query_present": "example",
      "key": "/pricing",
      "key_name": "X-Api-Key",
      "requests": 128,
      "period": 1,
      "algorithm": "sliding_window",
      "action": "block",
      "mitigation_timeout": 0,
      "per_node_divisor": true
    }
  ],
  "updated_at": "2026-10-27T09:30:00Z"
}
```

## Update edge settings

```http
PATCH /v1/orgs/{org}/zones/{zone}/edge
```

Changes any subset of the edge settings. Nested objects (`access_gate`, `hsts`, `origin`…) are merged field by field and lists are replaced. The settings apply to every proxied name of the zone, except what a name overrides in `hosts`.

`hosts` is a JSON merge patch (RFC 7396) per name: `{"hosts": {"shop.example.com": {"stack": "nextjs"}}}` is merged with that name's own settings; `null` for a name removes its overrides, `null` for a key returns it to the zone's value (also nested, e.g. `"access_gate": {"enabled": null}`), and `"hosts": null` removes them all. Names are lowercased without a trailing dot, must be the zone or below it, and up to 100 fit. A name without a proxied record is accepted with a warning (the edge does not serve it).

**Required role:** `tech` or higher.

**Change controls:** [`dry_run`](https://developers.openwaf.dev/api/#configuration-changes) · [`If-Match`](https://developers.openwaf.dev/api/#configuration-changes) (optional) · [`X-Change-Reason`](https://developers.openwaf.dev/api/#configuration-changes) (required)

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `X-Change-Reason` | header | string | Yes | Reason of the change: 1-500 UTF-8 characters after trimming spaces, no control characters. If it is missing or invalid: 422 `reason_required`. 1 to 500 characters. |
| `If-Match` | header | string | No | Expected configuration version, e.g. `"12"` (`W/` accepted). If it is not the current one: 412 `version_conflict`. |
| `dry_run` | query | string | No | `1`/`true` simulates the change: answers 200 with the plan (`Applied`) and saves nothing. `0`/`false` accepted; any other value: 400. One of `1`, `true`, `0`, `false`. |

#### Request body

Content type `application/json`. Schema: [ZoneEdgeSettings](https://developers.openwaf.dev/api/schemas/#zone-edge-settings).

| Field | Type | Required | Description |
|---|---|---|---|
| `zone_id` | string (uuid) | No | Zone id. |
| `mode` | string | No | One of `active`, `log`, `off`. Default `active`. |
| `waf_level` | string | No | One of `off`, `low`, `medium`, `high`. Default `medium`. |
| `rate_limit_rpm` | integer | No | Basic limit per IP and minute; 0 = off. Default `0`. Minimum 0. |
| `timeout_ms` | integer | No | Default `0`. Minimum 0. |
| `enable_feed` | boolean | No | Default `true`. |
| `stack` | string | No | One of `generic`, `wordpress`, `nextjs`, `static`. Default `generic`. |
| `block_page_mode` | string | No | One of `nginx`, `branded`, `apache`, `generic`, `custom`. Default `branded`. |
| `fake_server_header` | string | No | Default `""`. |
| `block_page_html` | string | No | At most 262144 characters. |
| `access_gate` | object | No |  |
| `access_gate.enabled` | boolean | No | Default `false`. |
| `access_gate.path` | string | No | Default `/`. |
| `access_gate.ttl_hours` | integer | No | Default `0`. Minimum 0. |
| `access_gate.emails` | array of string | No | Email addresses allowed through the access gate. |
| `max_body_mb` | integer | No | Default `0`. Minimum 0. |
| `redirect_allow_hosts` | array of string | No |  |
| `cors_allow_origins` | array of string | No |  |
| `hsts` | object | No |  |
| `hsts.max_age` | integer | No | Default `0`. From 0 to 63072000. |
| `hsts.include_subdomains` | boolean | No |  |
| `hsts.preload` | boolean | No |  |
| `compression` | boolean | No | Default `false`. |
| `origin` | object | No | The site's upstream URL = scheme://<record content>[:port] (IPv6 in brackets). |
| `origin.scheme` | string | No | One of `http`, `https`. Default `https`. |
| `origin.port` | integer | No | 0 = the scheme's default. Default `0`. From 0 to 65535. |
| `origin.tls_verify` | string | No | Verification of an https origin's certificate: full encrypts without verifying; full_strict verifies the chain (public roots + OpenWAF Origin CA), dates, name and revocation (failure = 526). New zones: full_strict; zones created before the feature: full. Only affects https origins. One of `full`, `full_strict`. |
| `origin.server_name` | string | No | SNI and verification name; empty = the origin host (or the site's domain if the origin is an IP). At most 253 characters. |
| `origin.client_cert` | boolean | No | The edge presents its client certificate to the origin (mTLS). Default `false`. |
| `hosts` | [HostSettingsMap](https://developers.openwaf.dev/api/schemas/#host-settings-map) | No | Own settings of some names of the zone: each name's site uses the zone's settings merged with its own field by field. `{}` when there are none (always present in answers). In a PATCH it is a JSON merge patch (RFC 7396) per name. |
| `redirects` | array of object | No | Redirect rules: the edge answers `status` with `Location` = `target` (+ the rest of the path after `path_prefix` if `preserve_path`, + the query string if `preserve_query`) without reaching the origin. The longest prefix of the name wins. `[]` when there are none (always present in answers). In a PATCH the list is replaced. The name needs a proxied record. At most 50 items. |
| `redirects[].host` | string | Yes | Full name in the zone (the zone or below it, no wildcard); lowercased. |
| `redirects[].path_prefix` | string | No | Starts with /; no ?, #, spaces or control characters. Stored in canonical form (non-ASCII and reserved characters as uppercase %XX: /año → /a%C3%B1o). /blog matches /blog and /blog/x, not /blogging. Default `/`. At most 1024 characters. |
| `redirects[].target` | string | Yes | Absolute http(s) URL without credentials, fragment or spaces; may have a query string. Stored with a lowercase scheme and non-ASCII path and query characters as %XX; the host in ASCII (punycode). It cannot point to a path that the rule itself captures. At most 2048 characters. |
| `redirects[].preserve_path` | boolean | No | Default `true`. |
| `redirects[].preserve_query` | boolean | No | Default `true`. |
| `redirects[].status` | integer | No | One of `301`, `302`, `307`, `308`. Default `301`. |
| `cache` | [EdgeCache](https://developers.openwaf.dev/api/schemas/#edge-cache) | No | Response cache at the edge, off by default. In a PATCH it is a JSON merge patch: objects are merged, lists (`rules`, `query.params`) are replaced, `"dev_mode_until": null` turns development mode off and `"cache": null` restores the defaults. |
| `cache.enabled` | boolean | No | Without rules only static extensions are eligible (never HTML or JSON); the origin's Cache-Control is respected. The WAF always runs first. Default `false`. |
| `cache.dev_mode_until` | string (date-time) or null | No | Development mode: the cache is not used until then (nothing is deleted); null = off. |
| `cache.query` | [CacheQuery](https://developers.openwaf.dev/api/schemas/#cache-query) | No |  |
| `cache.query.mode` | string | Yes | all: the whole query string (Standard); ignore: no query string in the cache key (static extensions only); none: only requests without a query string use the cache; include/exclude: only, or all but, the `params`. One of `all`, `ignore`, `none`, `include`, `exclude`. |
| `cache.query.sort` | boolean | No | Default `false`. |
| `cache.query.params` | array of string | No | Parameter names (include/exclude); a trailing * matches a prefix (utm_*). At most 32 items. |
| `cache.browser_ttl` | integer | No | Minimum browser TTL in seconds (a longer max-age from the origin wins); 0 = respect the origin. Default `0`. From 0 to 31536000. |
| `cache.deception_armor` | boolean | No | Do not store responses whose Content-Type does not match the extension. Default `true`. |
| `cache.serve_stale` | [CacheServeStale](https://developers.openwaf.dev/api/schemas/#cache-serve-stale) | No |  |
| `cache.serve_stale.while_revalidate` | boolean | No | Serve stale (UPDATING) while revalidating in the background. Default `true`. |
| `cache.serve_stale.on_error_max_s` | integer | No | Seconds after expiry during which a stale response is served if the origin fails (0 = never). Default `86400`. From 0 to 2592000. |
| `cache.max_object_mb` | integer | No | Default `100`. From 1 to 512. |
| `cache.rules` | array of [EdgeCacheRule](https://developers.openwaf.dev/api/schemas/#edge-cache-rule) | No | In order; if several match, the last one wins for each setting. At most 25 items. |
| `cache.rules[].id` | string | No | Unique; r1, r2… by default. Pattern `^[A-Za-z0-9_-]{1,64}$`. |
| `cache.rules[].hosts` | array of string | No | Names of the zone it applies to (empty = all; not allowed in a name's own list). |
| `cache.rules[].match` | object | No | Every list present must match, and within a list any value; without lists it matches everything (GET and HEAD only). |
| `cache.rules[].match.path_prefix` | array of string | No | The path starts with the value (canonical form). At most 32 items. |
| `cache.rules[].match.path_exact` | array of string | No | At most 32 items. |
| `cache.rules[].match.extensions` | array of string | No | Without the dot, lowercase; "" = no extension. At most 32 items. |
| `cache.rules[].match.cookie_present` | array of string | No | Cookie name; a trailing * matches a prefix. At most 32 items. |
| `cache.rules[].match.query_present` | array of string | No | The query string has a parameter with that name (decoded); a trailing * matches a prefix. At most 32 items. |
| `cache.rules[].action` | string | Yes | bypass: never cache (no other parameters); cache: eligible even if the extension is not static. One of `bypass`, `cache`. |
| `cache.rules[].edge_ttl` | object | No |  |
| `cache.rules[].edge_ttl.mode` | string | No | override ignores the origin's Cache-Control, removes Set-Cookie and turns off the cache deception armor. One of `respect_origin`, `bypass_by_default`, `override`. |
| `cache.rules[].edge_ttl.ttl_s` | integer | No | Only with override (1..31536000). From 0 to 31536000. |
| `cache.rules[].edge_ttl.status` | array of object | No | At most 10 items. |
| `cache.rules[].edge_ttl.status[].from` | integer | No | From 100 to 599. |
| `cache.rules[].edge_ttl.status[].to` | integer | No | From 100 to 599. |
| `cache.rules[].edge_ttl.status[].ttl_s` | integer | No | -1 = do not store; 0 = store and always revalidate. From -1 to 31536000. |
| `cache.rules[].browser_ttl` | object | No |  |
| `cache.rules[].browser_ttl.mode` | string | No | One of `respect_origin`, `override`. |
| `cache.rules[].browser_ttl.ttl_s` | integer | No | From 0 to 31536000. |
| `cache.rules[].query` | [CacheQuery](https://developers.openwaf.dev/api/schemas/#cache-query) | No |  |
| `cache.rules[].query.mode` | string | Yes | all: the whole query string (Standard); ignore: no query string in the cache key (static extensions only); none: only requests without a query string use the cache; include/exclude: only, or all but, the `params`. One of `all`, `ignore`, `none`, `include`, `exclude`. |
| `cache.rules[].query.sort` | boolean | No | Default `false`. |
| `cache.rules[].query.params` | array of string | No | Parameter names (include/exclude); a trailing * matches a prefix (utm_*). At most 32 items. |
| `cache.rules[].serve_stale` | [CacheServeStale](https://developers.openwaf.dev/api/schemas/#cache-serve-stale) | No |  |
| `cache.rules[].serve_stale.while_revalidate` | boolean | No | Serve stale (UPDATING) while revalidating in the background. Default `true`. |
| `cache.rules[].serve_stale.on_error_max_s` | integer | No | Seconds after expiry during which a stale response is served if the origin fails (0 = never). Default `86400`. From 0 to 2592000. |
| `cache.rules[].vary_bypass_if_present` | array of string | No | Only with cache. Request headers (stored lowercase; not accept-encoding, accept or origin): a request carrying any of them is neither looked up nor stored (DYNAMIC); the origin's Vary may list them without causing BYPASS. Changing the list moves the name's cache to new keys. At most 32 items. |
| `early_hints` | [EdgeEarlyHints](https://developers.openwaf.dev/api/schemas/#edge-early-hints) | No | 103 Early Hints, off by default. In a PATCH it is a JSON merge patch: fields are merged, `extra_origins` is replaced and `"early_hints": null` restores the defaults. |
| `early_hints.enabled` | boolean | No | Answer 103 to HTML navigations that go to the origin. Default `false`. |
| `early_hints.mode` | string | No | Where links are learned from: the origin's Link headers, the first 32 KiB of the HTML, or both. One of `origin_links`, `html_head`, `both`. Default `both`. |
| `early_hints.extra_origins` | array of string | No | Origins besides the site's own whose resources can be announced: `https://host[:port]`, without a path. They are normalized (spaces, lowercase, a trailing `/` and `:443` removed); duplicates or non-https values answer 422. At most 10 items. |
| `ai_agents` | [ZoneEdgeAIAgents](https://developers.openwaf.dev/api/schemas/#zone-edge-ai-agents) | No |  |
| `ai_agents.enabled` | boolean | No | Default `false`. |
| `ai_agents.mode` | string | No | log: only counts what it would do (enforced=false). One of `log`, `enforce`. Default `log`. |
| `ai_agents.spoofed_challenge` | string | No | enforce: only verified crawlers skip the proof-of-work challenge. One of `log`, `enforce`. Default `log`. |
| `ai_agents.template` | string | No | One of `search_ok_block_training`, `verified_only`, `block_unverified_automation`. |
| `ai_agents.policy` | object | No |  |
| `ai_agents.policy.ai_training` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.ai_search` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.ai_user_agent` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.search_engine` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.unverified_automation` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.spoofed` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.unverified_bot` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.trap_hit` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.secret_probe` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.robots_violation` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.policy.agent_burst` | string | No | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.bot_overrides` | array of object | No | At most 100 items. |
| `ai_agents.bot_overrides[].bot_id` | string | Yes |  |
| `ai_agents.bot_overrides[].action` | string | Yes | One of `allow`, `log`, `challenge`, `block`, `tarpit`. |
| `ai_agents.trap` | object | No | Trap link; the zone's HMAC key is generated when published and never leaves the API. |
| `ai_agents.trap.enabled` | boolean | No |  |
| `ai_agents.tarpit` | object | No |  |
| `ai_agents.tarpit.enabled` | boolean | No |  |
| `ai_agents.robots` | object | No |  |
| `ai_agents.robots.enabled` | boolean | No |  |
| `ai_agents.robots.content_signals` | object | No |  |
| `ai_agents.robots.content_signals.search` | string | No | One of `yes`, `no`, `""`. |
| `ai_agents.robots.content_signals.ai_input` | string | No | One of `yes`, `no`, `""`. |
| `ai_agents.robots.content_signals.ai_train` | string | No | One of `yes`, `no`, `""`. |
| `ai_agents.markdown` | object | No |  |
| `ai_agents.markdown.enabled` | boolean | No |  |
| `ai_agents.block_response` | object | No |  |
| `ai_agents.block_response.status` | integer | No | One of `401`, `402`, `403`, `404`, `410`, `429`, `451`, `503`. Default `403`. |
| `ai_agents.block_response.message` | string | No | At most 1024 characters. |
| `ai_agents.endpoints` | array of object | No | At most 20 items. |
| `ai_agents.endpoints[].path_prefix` | string | No | At most 256 characters. Pattern `^/`. |
| `ai_agents.endpoints[].methods` | array of string | No | POST, PUT, PATCH, GET; with kind mcp, POST, GET and DELETE (with POST; empty = the three). |
| `ai_agents.endpoints[].max_body_bytes` | integer | No | 1-1048576; 0 with kind mcp. From 0 to 1048576. |
| `ai_agents.endpoints[].rpm_per_ip` | integer | No | From 0 to 100000. |
| `ai_agents.endpoints[].rpm_per_session` | integer | No | From 0 to 100000. |
| `ai_agents.endpoints[].session_header` | string | No |  |
| `ai_agents.endpoints[].session_cookie` | string | No |  |
| `ai_agents.endpoints[].injection_action` | string | No | Empty with kind mcp. One of `log`, `block`, `""`. |
| `ai_agents.endpoints[].limit_action` | string | No | Empty with kind mcp. One of `log`, `block`, `""`. |
| `ai_agents.endpoints[].kind` | string | No | Absent = AI endpoint; mcp = MCP server protected by MCP Shield (the AI endpoint fields at 0 or empty). One of `mcp`. |
| `ai_agents.endpoints[].mcp` | [AiMcpPolicy](https://developers.openwaf.dev/api/schemas/#ai-mcp-policy) | No |  |
| `ai_agents.endpoints[].mcp.methods_allowed` | array of string | No | Exact JSON-RPC methods or x/* prefixes; by default initialize, ping, notifications/*, tools/list, tools/call, resources/list, resources/read, resources/templates/list, prompts/list, prompts/get, completion/complete, logging/setLevel. 1 to 64 items. Items match `^[A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+)*(/\*)?$`. |
| `ai_agents.endpoints[].mcp.tools_allowed` | array of string | No | [] = any tool that is not denied. At most 200 items. Items match `^[A-Za-z0-9_.:/-]{1,128}$`. |
| `ai_agents.endpoints[].mcp.tools_denied` | array of string | No | At most 200 items. Items match `^[A-Za-z0-9_.:/-]{1,128}$`. |
| `ai_agents.endpoints[].mcp.max_request_kb` | integer | No | Default `256`. From 1 to 1024. |
| `ai_agents.endpoints[].mcp.inspect_requests` | string | No | One of `off`, `log`, `block`. Default `log`. |
| `ai_agents.endpoints[].mcp.inspect_responses` | string | No | One of `off`, `log`, `block`. Default `log`. |
| `ai_agents.endpoints[].mcp.rate_per_token` | integer | No | Requests per minute per token (Authorization); 0 = no limit. Default `0`. From 0 to 100000. |
| `rum` | [ZoneEdgeRUM](https://developers.openwaf.dev/api/schemas/#zone-edge-rum) | No | Real user measurement of Core Web Vitals, off by default. In a PATCH it is a JSON merge patch: the given fields replace the current ones (`exclude_paths` as a whole), a field set to null returns to its default and `"rum": null` restores the default object. It is validated even when off. |
| `rum.enabled` | boolean | No | The edge injects /__waf/rum.js in the site's HTML pages and receives its beacons at /__waf/rum. Default `false`. |
| `rum.sample_rate` | number | No | Fraction of page views that get the script, (0, 1]. Default `1`. Maximum 1. |
| `rum.exclude_paths` | array of string | No | Path prefixes that never get the script (spaces are trimmed; empty entries are dropped). Default `[]`. At most 50 items. Items match `^/[^?#]*$`. |
| `rate_rules` | array of [EdgeRateRule](https://developers.openwaf.dev/api/schemas/#edge-rate-rule) | No | Per-path rate limits, in evaluation order; none by default. In a PATCH the whole list is replaced (`[]` removes them all). Each name can have its own list in `hosts.<name>.rate_rules`. At most 25 items. |
| `rate_rules[].id` | string | No | Unique in the list; empty = derived from the name (login, login-2…). Events carry it as rule_name rate_rule:<id>. Pattern `^[a-z0-9][a-z0-9-]{0,39}$`. |
| `rate_rules[].name` | string | No | Name. At most 100 characters. |
| `rate_rules[].enabled` | boolean | No | Default `true`. |
| `rate_rules[].hosts` | array of string | No | Names of the zone it applies to; [] = all (always [] in a name's own list). |
| `rate_rules[].path_prefix` | string | No | Paths starting with it at a segment boundary; / = all. Stored lowercase, without a trailing /. At most 512 characters. |
| `rate_rules[].path_exact` | string | No | Only this path (a trailing / does not count). Stored lowercase. At most 512 characters. |
| `rate_rules[].methods` | array of string | No | Methods; [] = any. Default `[]`. At most 16 items. |
| `rate_rules[].header_present` | string | No | Only if the request has this header. At most 64 characters. |
| `rate_rules[].query_present` | string | No | Only if the request has this query parameter. At most 64 characters. |
| `rate_rules[].key` | string | No | What is counted: IP (IPv6 per /64), header, cookie or JSON body field (counted per IP when the value is missing). One of `ip`, `header`, `cookie`, `body_field`. Default `ip`. |
| `rate_rules[].key_name` | string | No | Header, cookie or path of the JSON field (a.b, up to 4 levels); empty with ip. At most 64 characters. |
| `rate_rules[].requests` | integer | Yes | Requests. From 1 to 1000000. |
| `rate_rules[].period` | integer | Yes | Seconds. From 1 to 86400. |
| `rate_rules[].algorithm` | string | No | One of `sliding_window`, `gcra`. Default `sliding_window`. |
| `rate_rules[].action` | string | No | One of `block`, `challenge`, `log`. Default `block`. |
| `rate_rules[].mitigation_timeout` | integer | No | Seconds the action keeps applying to the key after it exceeds the limit; 0 = only while it exceeds it. Default `0`. From 0 to 86400. |
| `rate_rules[].per_node_divisor` | boolean | No | Divide requests among the edge entry points that serve the site, so the limit applies to the total. Default `true`. |
| `updated_at` | string (date-time) | No | Last change. |

#### Response

- `200`: Settings saved (with X-Config-Version and ETag) or, with `dry_run`, the plan (`Applied`). Returns [ZoneEdgeSettings](https://developers.openwaf.dev/api/schemas/#zone-edge-settings) or [Applied](https://developers.openwaf.dev/api/schemas/#applied).

| Field | Type | Description |
|---|---|---|
| `zone_id` | string (uuid) | Zone id. |
| `mode` | string | One of `active`, `log`, `off`. Default `active`. |
| `waf_level` | string | One of `off`, `low`, `medium`, `high`. Default `medium`. |
| `rate_limit_rpm` | integer | Basic limit per IP and minute; 0 = off. Default `0`. Minimum 0. |
| `timeout_ms` | integer | Default `0`. Minimum 0. |
| `enable_feed` | boolean | Default `true`. |
| `stack` | string | One of `generic`, `wordpress`, `nextjs`, `static`. Default `generic`. |
| `block_page_mode` | string | One of `nginx`, `branded`, `apache`, `generic`, `custom`. Default `branded`. |
| `fake_server_header` | string | Default `""`. |
| `block_page_html` | string | At most 262144 characters. |
| `access_gate` | object |  |
| `access_gate.enabled` | boolean | Default `false`. |
| `access_gate.path` | string | Default `/`. |
| `access_gate.ttl_hours` | integer | Default `0`. Minimum 0. |
| `access_gate.emails` | array of string | Email addresses allowed through the access gate. |
| `max_body_mb` | integer | Default `0`. Minimum 0. |
| `redirect_allow_hosts` | array of string |  |
| `cors_allow_origins` | array of string |  |
| `hsts` | object |  |
| `hsts.max_age` | integer | Default `0`. From 0 to 63072000. |
| `hsts.include_subdomains` | boolean |  |
| `hsts.preload` | boolean |  |
| `compression` | boolean | Default `false`. |
| `origin` | object | The site's upstream URL = scheme://<record content>[:port] (IPv6 in brackets). |
| `origin.scheme` | string | One of `http`, `https`. Default `https`. |
| `origin.port` | integer | 0 = the scheme's default. Default `0`. From 0 to 65535. |
| `origin.tls_verify` | string | Verification of an https origin's certificate: full encrypts without verifying; full_strict verifies the chain (public roots + OpenWAF Origin CA), dates, name and revocation (failure = 526). New zones: full_strict; zones created before the feature: full. Only affects https origins. One of `full`, `full_strict`. |
| `origin.server_name` | string | SNI and verification name; empty = the origin host (or the site's domain if the origin is an IP). At most 253 characters. |
| `origin.client_cert` | boolean | The edge presents its client certificate to the origin (mTLS). Default `false`. |
| `hosts` | [HostSettingsMap](https://developers.openwaf.dev/api/schemas/#host-settings-map) | Own settings of some names of the zone: each name's site uses the zone's settings merged with its own field by field. `{}` when there are none (always present in answers). In a PATCH it is a JSON merge patch (RFC 7396) per name. |
| `redirects` | array of object | Redirect rules: the edge answers `status` with `Location` = `target` (+ the rest of the path after `path_prefix` if `preserve_path`, + the query string if `preserve_query`) without reaching the origin. The longest prefix of the name wins. `[]` when there are none (always present in answers). In a PATCH the list is replaced. The name needs a proxied record. At most 50 items. |
| `redirects[].host` | string | Full name in the zone (the zone or below it, no wildcard); lowercased. |
| `redirects[].path_prefix` | string | Starts with /; no ?, #, spaces or control characters. Stored in canonical form (non-ASCII and reserved characters as uppercase %XX: /año → /a%C3%B1o). /blog matches /blog and /blog/x, not /blogging. Default `/`. At most 1024 characters. |
| `redirects[].target` | string | Absolute http(s) URL without credentials, fragment or spaces; may have a query string. Stored with a lowercase scheme and non-ASCII path and query characters as %XX; the host in ASCII (punycode). It cannot point to a path that the rule itself captures. At most 2048 characters. |
| `redirects[].preserve_path` | boolean | Default `true`. |
| `redirects[].preserve_query` | boolean | Default `true`. |
| `redirects[].status` | integer | One of `301`, `302`, `307`, `308`. Default `301`. |
| `cache` | [EdgeCache](https://developers.openwaf.dev/api/schemas/#edge-cache) | Response cache at the edge, off by default. In a PATCH it is a JSON merge patch: objects are merged, lists (`rules`, `query.params`) are replaced, `"dev_mode_until": null` turns development mode off and `"cache": null` restores the defaults. |
| `cache.enabled` | boolean | Without rules only static extensions are eligible (never HTML or JSON); the origin's Cache-Control is respected. The WAF always runs first. Default `false`. |
| `cache.dev_mode_until` | string (date-time) or null | Development mode: the cache is not used until then (nothing is deleted); null = off. |
| `cache.query` | [CacheQuery](https://developers.openwaf.dev/api/schemas/#cache-query) |  |
| `cache.browser_ttl` | integer | Minimum browser TTL in seconds (a longer max-age from the origin wins); 0 = respect the origin. Default `0`. From 0 to 31536000. |
| `cache.deception_armor` | boolean | Do not store responses whose Content-Type does not match the extension. Default `true`. |
| `cache.serve_stale` | [CacheServeStale](https://developers.openwaf.dev/api/schemas/#cache-serve-stale) |  |
| `cache.max_object_mb` | integer | Default `100`. From 1 to 512. |
| `cache.rules` | array of [EdgeCacheRule](https://developers.openwaf.dev/api/schemas/#edge-cache-rule) | In order; if several match, the last one wins for each setting. At most 25 items. |
| `early_hints` | [EdgeEarlyHints](https://developers.openwaf.dev/api/schemas/#edge-early-hints) | 103 Early Hints, off by default. In a PATCH it is a JSON merge patch: fields are merged, `extra_origins` is replaced and `"early_hints": null` restores the defaults. |
| `early_hints.enabled` | boolean | Answer 103 to HTML navigations that go to the origin. Default `false`. |
| `early_hints.mode` | string | Where links are learned from: the origin's Link headers, the first 32 KiB of the HTML, or both. One of `origin_links`, `html_head`, `both`. Default `both`. |
| `early_hints.extra_origins` | array of string | Origins besides the site's own whose resources can be announced: `https://host[:port]`, without a path. They are normalized (spaces, lowercase, a trailing `/` and `:443` removed); duplicates or non-https values answer 422. At most 10 items. |
| `ai_agents` | [ZoneEdgeAIAgents](https://developers.openwaf.dev/api/schemas/#zone-edge-ai-agents) |  |
| `rum` | [ZoneEdgeRUM](https://developers.openwaf.dev/api/schemas/#zone-edge-rum) | Real user measurement of Core Web Vitals, off by default. In a PATCH it is a JSON merge patch: the given fields replace the current ones (`exclude_paths` as a whole), a field set to null returns to its default and `"rum": null` restores the default object. It is validated even when off. |
| `rum.enabled` | boolean | The edge injects /__waf/rum.js in the site's HTML pages and receives its beacons at /__waf/rum. Default `false`. |
| `rum.sample_rate` | number | Fraction of page views that get the script, (0, 1]. Default `1`. Maximum 1. |
| `rum.exclude_paths` | array of string | Path prefixes that never get the script (spaces are trimmed; empty entries are dropped). Default `[]`. At most 50 items. Items match `^/[^?#]*$`. |
| `rate_rules` | array of [EdgeRateRule](https://developers.openwaf.dev/api/schemas/#edge-rate-rule) | Per-path rate limits, in evaluation order; none by default. In a PATCH the whole list is replaced (`[]` removes them all). Each name can have its own list in `hosts.<name>.rate_rules`. At most 25 items. |
| `updated_at` | string (date-time) | Last change. |

Response headers: `ETag`, `X-Config-Version`.

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `409` | `zone_merged`: the zone is merged into another one. |
| `412` | `version_conflict`: If-Match does not match the current version. |
| `422` | Validation error (`invalid`) or missing or invalid reason (`reason_required`). |

#### Example

**curl**


```sh
curl -X PATCH "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/edge" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "X-Change-Reason: Enable the WAF in block mode" \
  -H 'If-Match: "12"' \
  -H "Content-Type: application/json" \
  --data '{
    "mode": "active",
    "waf_level": "high",
    "hsts": {
      "max_age": 31536000,
      "include_subdomains": true
    }
  }'
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.updateZoneEdge({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  body: {
    mode: "active",
    waf_level: "high",
    hsts: {
      max_age: 31536000,
      include_subdomains: true
    }
  }
}, {
  reason: "Enable the WAF in block mode",
  ifMatch: 12
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.update_zone_edge(
    zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
    body={
        "mode": "active",
        "waf_level": "high",
        "hsts": {
            "max_age": 31536000,
            "include_subdomains": True
        }
    },
    reason="Enable the WAF in block mode",
    if_match=12,
)
```



```json
{
  "zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  "mode": "active",
  "waf_level": "medium",
  "rate_limit_rpm": 0,
  "timeout_ms": 0,
  "enable_feed": true,
  "stack": "generic",
  "block_page_mode": "branded",
  "fake_server_header": "",
  "block_page_html": "",
  "access_gate": {
    "enabled": false,
    "path": "/checkout",
    "ttl_hours": 0,
    "emails": [
      "ops@example.com"
    ]
  },
  "max_body_mb": 0,
  "redirect_allow_hosts": [
    "www.example.com"
  ],
  "cors_allow_origins": [
    "https://app.example.com"
  ],
  "hsts": {
    "max_age": 0,
    "include_subdomains": false,
    "preload": false
  },
  "compression": false,
  "origin": {
    "scheme": "https",
    "port": 0,
    "tls_verify": "full",
    "server_name": "",
    "client_cert": false
  },
  "hosts": {},
  "redirects": [
    {
      "host": "www.example.com",
      "path_prefix": "/api/",
      "target": "https://www.example.com/new",
      "preserve_path": true,
      "preserve_query": true,
      "status": 301
    }
  ],
  "cache": {
    "enabled": false,
    "dev_mode_until": "2026-10-27T09:30:00Z",
    "query": {
      "mode": "all",
      "sort": false,
      "params": [
        "example"
      ]
    },
    "browser_ttl": 0,
    "deception_armor": true,
    "serve_stale": {
      "while_revalidate": true,
      "on_error_max_s": 86400
    },
    "max_object_mb": 100,
    "rules": [
      {
        "id": "example",
        "hosts": [],
        "match": {},
        "action": "block",
        "edge_ttl": {},
        "browser_ttl": {},
        "query": {},
        "serve_stale": {},
        "vary_bypass_if_present": []
      }
    ]
  },
  "early_hints": {
    "enabled": false,
    "mode": "both",
    "extra_origins": [
      "https://cdn.example.com"
    ]
  },
  "ai_agents": {
    "enabled": false,
    "mode": "log",
    "spoofed_challenge": "log",
    "template": "search_ok_block_training",
    "policy": {
      "ai_training": "allow",
      "ai_search": "allow",
      "ai_user_agent": "allow",
      "search_engine": "allow",
      "unverified_automation": "allow",
      "spoofed": "allow",
      "unverified_bot": "allow",
      "trap_hit": "allow",
      "secret_probe": "allow",
      "robots_violation": "allow",
      "agent_burst": "allow"
    },
    "bot_overrides": [
      {
        "bot_id": "948186e3-4508-48d6-a001-c683a3ec9626",
        "action": "block"
      }
    ],
    "trap": {
      "enabled": false
    },
    "tarpit": {
      "enabled": false
    },
    "robots": {
      "enabled": false,
      "content_signals": {
        "search": "yes",
        "ai_input": "yes",
        "ai_train": "yes"
      }
    },
    "markdown": {
      "enabled": false
    },
    "block_response": {
      "status": 403,
      "message": ""
    },
    "endpoints": [
      {
        "path_prefix": "/api/",
        "methods": [
          "POST"
        ],
        "max_body_bytes": 0,
        "rpm_per_ip": 0,
        "rpm_per_session": 0,
        "session_header": "X-Session-Id",
        "session_cookie": "session",
        "injection_action": "log",
        "limit_action": "log",
        "kind": "mcp",
        "mcp": {}
      }
    ]
  },
  "rum": {
    "enabled": false,
    "sample_rate": 1,
    "exclude_paths": []
  },
  "rate_rules": [
    {
      "id": "example",
      "name": "example.com",
      "enabled": true,
      "hosts": [
        "www.example.com"
      ],
      "path_prefix": "/auth/login",
      "path_exact": "/login",
      "methods": [
        "POST"
      ],
      "header_present": "X-Api-Key",
      "query_present": "example",
      "key": "/pricing",
      "key_name": "X-Api-Key",
      "requests": 128,
      "period": 1,
      "algorithm": "sliding_window",
      "action": "block",
      "mitigation_timeout": 0,
      "per_node_divisor": true
    }
  ],
  "updated_at": "2026-10-27T09:30:00Z"
}
```

## Get effective configuration of a name

```http
GET /v1/orgs/{org}/zones/{zone}/hosts/{host}/effective
```

Shows the configuration that one name of the zone gets at the edge, exactly as the edge nodes receive it, the zone's settings merged with the name's own, the rules that apply to it and its redirects. The `ETag` header carries the zone's version.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `host` | path | string | Yes | Full name (the zone or below it). |

#### Response

- `200`: Returns [EffectiveConfig](https://developers.openwaf.dev/api/schemas/#effective-config).

| Field | Type | Description |
|---|---|---|
| `zone_id` | string (uuid) | Zone id. |
| `host` | string |  |
| `proxied` | boolean | false: the name has no proxied record and `site` is a preview without upstream_url. |
| `items` | array of object |  |
| `items[].site` | [EdgeSite](https://developers.openwaf.dev/api/schemas/#edge-site) or any or null |  |

Response headers: `ETag`.

#### Errors

| Status | Description |
|---|---|
| `404` | Unknown zone, or `host_not_in_zone`: the name is neither the zone nor below it. |
| `422` | Validation error (`invalid`). |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/hosts/www.example.com/effective" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getHostEffective({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  host: "www.example.com"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_host_effective(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", host="www.example.com")
```



```json
{
  "zone_id": "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  "host": "www.example.com",
  "proxied": false,
  "items": [
    {
      "site": {
        "id": 0,
        "hostname": "www.example.com",
        "upstream_url": "https://203.0.113.10",
        "rules": [],
        "redirects": [],
        "cache": {},
        "early_hints": {},
        "rum": {},
        "rate_rules": []
      }
    }
  ]
}
```
