In the portal, environments using the v1 load balancer can also configure a web application firewall. This is done on the firewall page in the load balancer modal.
To get there:
- Environments from the main, left-hand navigation.
- Select the environment from the first column.
- Scroll down to the services tile, and click on the manage tab of the load balancer.
- Select the Firewall page.
The controller must be set to Enable Custom Config for the firewall config to be applied.
After enabling the Firewall Configuration, the user sees an example Rule entry.
Firewall Rule Fields
Each Rule is comprised of the following fields:
Field | Description |
|---|---|
description | A text description of the rule, providing context or notes about what the rule is intended to do. |
skip | A boolean value indicating whether the rule should be skipped ( |
type | Specifies the action of the rule: |
expires | Optional expiration date for the rule. If set, the rule is automatically disabled after this date. |
match | Determines how conditions are evaluated: |
sample | Optional percentage (0–100) controlling what portion of requests the rule applies to. |
conditions | An array of conditions that must be met for the rule to apply. Each condition contains: |
|
|
|
|
|
|
|
|
|
|
Supported Types
The currently supported condition types are listed below. Note that the HTTP-aware types (`http-url-match`, `http-method-match`, `http-header-match`, `http-body-match`) require an HTTP or HTTPS controller. On TCP or UDP controllers, only `ip-match` and `geo-match` apply.
Type | Description |
|---|---|
| Filters traffic based on specific IP addresses or ranges using CIDR. |
| Filters traffic based on geographic origin of the request. |
| Matches requests based on the HTTP URL path, excluding the domain name. |
| Evaluates HTTP requests by their method (GET, POST, PUT, etc). |
| Matches requests based on HTTP header values. |
| Matches requests based on content within the HTTP request body. |
Supported Operators
Operator | Description |
|---|---|
| Equal to |
| Not equal to |
| Contains |
| Does not contain |
| Starts with |
| Does not start with |
| Greater than |
| Less than |
| Greater than or equal to |
| Less than or equal to |
The numeric operators (`>`, `<`, `>=`, `<=`) compare numerically and are most useful against header values that carry numbers (e.g., `Content-Length`). Other operators perform string comparison.
Rule Types
Type | Behavior |
|---|---|
| Permits traffic that matches the conditions. |
| Disconnects the connection for traffic matching the conditions. |
| Returns an HTTP 403 Forbidden response for traffic matching conditions. |
Customizing the Block Response
When using `block`, the HTTP response code can be customized on a per-rule basis using the `response.http_code` field:
{
"description": "Return 429 for rate-limited paths",
"type": "block",
"match": "any",
"response": {
"http_code": 429
},
"conditions": [
{ "type": "http-url-match", "operator": "^=", "value": "/api/" }
]
}If `response.http_code` is omitted, `block` returns 403.
Evaluation Order
Rules are evaluated in array order. The first rule whose conditions match determines the action taken on the request. There is no implicit default `allow` or `deny`, if traffic does not match a block it simply follows through with what the load balancer or controller would have done otherwise.
For example: "allow some traffic, then deny everything else" is expressed as a narrow `allow` rule first, followed by a broad `deny` rule with `0.0.0.0/0` and `::/0`. The order matters: reversing them would deny all traffic before the `allow` rule could fire.
Example Firewall Configs
An example use case might be using http-url-match and ip-match together to constrain traffic from any non-VPN IP into a resource while simultaneously defining a DENY block for all IPs. Approved IPs requesting the resource would be able to connect, while all other traffic would be blocked. This example also uses sample to apply the rule to all requests and sets an expiration date.
[ { "description": "Only allow VPN traffic", "skip": false, "type": "allow", "match": "any", "sample": 100, "expires": "2026-12-31T23:59:59Z", "conditions": [ { "type": "http-url-match", "operator": "==", "value": "/resource-path" }, { "type": "ip-match", "operator": "==", "value": "10.10.24.0/24" } ] }, { "description": "Deny all other traffic", "skip": false, "type": "deny", "match": "any", "conditions": [ { "type": "ip-match", "operator": "==", "value": "0.0.0.0/0" }, { "type": "ip-match", "operator": "==", "value": "::/0" } ] }]Using key to target a specific HTTP header, combined with regex for pattern matching:
[ { "description": "Block requests with suspicious user agents", "skip": false, "type": "block", "match": "any", "conditions": [ { "type": "http-header-match", "operator": "*=", "key": "User-Agent", "value": "^(curl|wget|python-requests)", "regex": true } ] }]Using block to return a 403 for specific IPs, with a contains operator for URL matching:
[ { "description": "Block specific IPs from admin paths", "skip": false, "type": "block", "match": "all", "conditions": [ { "type": "ip-match", "operator": "==", "value": "50.234.222.10" }, { "type": "http-url-match", "operator": "*=", "value": "/admin" } ] }, { "description": "Deny suspicious IPv6 address", "skip": false, "type": "deny", "match": "any", "conditions": [ { "type": "ip-match", "operator": "==", "value": "2600:6b4a:223f:93cf:84a1:4afd:9221:8988" } ] }]