Custom WAF rules
Match edge traffic by path, headers, geo, IP, and more, then log, block, challenge, bypass, or redirect.
Custom Web Application Firewall (WAF) rules let you control how the edge handles incoming requests before they reach your deployment. Rules are project-scoped: they apply to every hostname that serves that project (preview and production URLs, and custom domains).
Easel saves rule changes on the project and the edge picks them up after a short delay. You do not need to redeploy to update rules.
Where to configure
- Open your workspace and select the project.
- Go to Project settings.
- Scroll to Custom WAF rules.
- Add rules, toggle Active, reorder with Move up / Move down, and click Save rules.
Clearing all rules and saving stores an empty ruleset (no custom WAF behavior).
How matching works
- Rule order matters: Easel evaluates active rules from top to bottom. The first rule whose conditions match applies; lower rules are not considered for that request.
- Condition groups (OR): a rule can have multiple groups. If any group matches, the rule matches.
- Conditions inside a group (AND): every condition in a group must be true for that group to match.
- Negate: inverts a single condition after the operator runs. Prefer explicit operators such as Does not equal or Is not any of for new rules.
Conditions
Each condition has a type, an operator, and either a single value or a values list (one per line) for Is any of / Is not any of. Some types use a key (header name, query parameter name, or cookie name).
Types
| Type | What is matched |
|---|---|
| Request path (normalized) | URL path with consistent trailing-slash handling. |
| Raw path | Path as received, without that normalization step. |
| Route pattern | The route pattern from your deployment that matched this request. |
| Method | HTTP method, compared in uppercase (GET, POST, …). |
| User-Agent | The User-Agent header. |
| Request header | A header by key, or legacy Name:expected in value when key is empty (first : separates name from the substring to compare). |
| Query string (full) | Everything after ? in the URL. |
| Query parameter | A single query parameter; set key to the parameter name. |
| Cookie | A named cookie (key), or the raw Cookie header when key is empty (substring style). |
| Hostname | Host without port. |
| Environment | Deployment environment (production or preview). |
| Protocol | Request protocol version (for example HTTP/2.0). |
| IP / CIDR | Client IP after trusted proxy handling. Equals / Does not equal accept one IP or one CIDR. Is any of / Is not any of accept a list of IPs and/or CIDRs. String operators match against the textual IP form. |
| Edge region | The edge region that handled the request (also exposed as X-Easel-Region when present). |
| Geo continent / country | Continent or country derived from the client IP. Comparisons are case-normalized to uppercase. |
| AS number | Autonomous system number (ASN) derived from the client IP. |
| Server action (Next.js) | Matches Next-Action (and falls back to Next-Router-State-Tree if empty). |
Operators
| Label | Operand |
|---|---|
| Equals | Single value |
| Does not equal | Single value |
| Is any of | Values (one per line); equality against the haystack |
| Is not any of | Values (one per line) |
| Starts with / Does not start with | Single value |
| Ends with / Does not end with | Single value |
| Contains / Does not contain | Single value |
| Does not contain (legacy) | Same as Does not contain; kept for older saved rules |
| Matches expression / Does not match expression | Regular expression. Validate with a Log rule first; patterns that work in JavaScript may still fail to compile on the edge. |
For a header with only a name in legacy form (no :value), Equals / Does not contain (legacy) mean present / absent for that header.
Geo and ASN
Geo country, geo continent, and ASN conditions use IP address data powered by IPLocate.io (CC BY-SA 4.0).
Trusted client IP
For IP, geo, and ASN conditions, the edge resolves the client IP from the connection that reaches Easel CDN. Easel honors X-Forwarded-For only when the request arrives through Easel’s trusted edge path (the same model used for verified bot handling). Easel ignores client-supplied forwarding headers from untrusted peers.
Actions (Then)
| Action | Effect |
|---|---|
| Log | Records a firewall observation for analytics and continues to your app. Use this to validate a rule before switching to a stricter action. |
| Deny (403) | Responds with 403 Forbidden; the request does not proceed to your deployment. |
| Challenge (PoW) | Serves a browser proof-of-work checkpoint (same family of flow as Attack challenge mode). After the visitor succeeds, the edge sets a session cookie (about one hour); repeat visits from that browser skip that challenge until the session expires. |
| Bypass attack challenge | For matching requests, skips project-wide Attack challenge mode only. Other platform protections still apply. See WAF and attack challenge. |
| Redirect | Responds with an HTTP redirect. Set HTTP status (3xx) and Location (absolute https://… URL recommended). |
Challenge rules use the same verification endpoint as Attack challenge mode. The edge always allows /.well-known/easel-challenge/* so verification and the session cookie can complete without the rule matching those paths in a loop.
Recommended rollout
- Create a rule with Log and confirm traffic in observability (see below).
- Refine conditions until only the traffic you care about matches.
- Change Then to Deny, Challenge, Bypass, or Redirect as needed.
Observe first, then enforce.
Firewall observability
In the dashboard, open your project’s Edge or Observability views where firewall and edge request metrics appear. Custom WAF decisions show actions such as log, deny, challenge, bypass, and redirect.
When visitors complete or fail proof-of-work at /.well-known/easel-challenge/verify, you may also see challenge pass and challenge fail counts. Those counts reflect verification outcomes, not every page view.
Current limitations
- No per-rule rate limiting in the dashboard yet (rules cannot throttle by request rate).
- No time-based IP ban from rule settings in the product UI today.
- Rules are ordered; there is no separate priority number beyond list order.
- TLS fingerprint matching is not available yet.
- The edge evaluates regex patterns; patterns that are valid in JavaScript may still fail to compile (validate with a Log rule first).
Related guides
- WAF and attack challenge: how custom rules interact with Attack challenge mode and Bypass
- Troubleshooting: 403 and challenge symptoms