Loading...
Loading...
Vercel Firewall and security expert guidance. Use when configuring DDoS protection, WAF rules, rate limiting, bot filtering, IP allow/block lists, OWASP rulesets, Attack Challenge Mode, or any security configuration on the Vercel platform.
npx skill4agent add vercel-labs/vercel-plugin vercel-firewall{
"name": "Block WordPress scanners",
"description": "Block common WordPress probe paths",
"active": true,
"conditionGroup": [
{
"conditions": [
{
"type": "path",
"op": "re",
"value": "^/wp-(admin|login|content|includes)/"
}
]
}
],
"action": {
"mitigate": {
"action": "deny"
}
}
}conditionGroup| Type | Description | Extra Fields |
|---|---|---|
| URL path | |
| HTTP method | |
| Hostname | |
| Client IP (supports CIDR) | |
| User-Agent string | |
| Request header value | |
| Query string parameter | |
| Cookie value | |
| ISO country code (e.g., | |
| Continent code (e.g., | |
| State/province code | |
| City name | |
| ASN | |
| JA4 TLS fingerprint | |
| JA3 TLS fingerprint | |
| Resolved path after routing | |
| Matched route pattern | |
| Raw unparsed path | |
| Vercel edge region code | |
| http/https | |
| URL scheme | |
| Deployment environment | |
| Specific bot name | |
| Bot category | |
| Next.js Server Action ID |
| Op | Meaning |
|---|---|
| Equals |
| Not equals |
| Regex match |
| Starts with |
| Ends with |
| Contains |
| In array |
| Not in array |
| Exists |
| Not exists |
| Greater than (or equal) |
| Less than (or equal) |
neg: truekeyheaderquerycookie| Action | Description |
|---|---|
| Log only, allow traffic |
| Block request (403) |
| JavaScript browser challenge |
| Skip all subsequent WAF rules |
| Apply rate limiting (requires |
| Redirect (requires |
actionDuration{
"action": {
"mitigate": {
"action": "deny",
"actionDuration": "1h",
"bypassSystem": false,
"logHeaders": ["user-agent", "x-forwarded-for"],
"redirect": {
"location": "https://example.com/blocked",
"permanent": false
}
}
}
}{
"name": "Block OFAC Sanctioned Countries",
"active": true,
"conditionGroup": [
{
"conditions": [
{
"type": "geo_country",
"op": "inc",
"value": ["CU", "IR", "KP", "RU", "SY"]
}
]
}
],
"action": {
"mitigate": { "action": "deny" }
}
}{
"name": "Require API Key",
"active": true,
"conditionGroup": [
{
"conditions": [
{
"type": "header",
"op": "nex",
"key": "x-api-key"
},
{
"type": "path",
"op": "pre",
"value": "/api/"
}
]
}
],
"action": {
"mitigate": { "action": "deny" }
}
}{
"name": "Block Known Malicious JA4",
"active": true,
"conditionGroup": [
{
"conditions": [
{
"type": "ja4_digest",
"op": "eq",
"value": "t13d1516h2_8daaf6152771_b0da82dd1658"
}
]
}
],
"action": {
"mitigate": { "action": "deny", "actionDuration": "1h" }
}
}{
"name": "Block Known Datacenter ASNs",
"active": true,
"conditionGroup": [
{
"conditions": [
{
"type": "geo_as_number",
"op": "inc",
"value": ["14618", "16509", "15169"]
}
]
}
],
"action": {
"mitigate": { "action": "deny" }
}
}{
"name": "Challenge cURL",
"active": true,
"conditionGroup": [
{
"conditions": [
{ "type": "user_agent", "op": "re", "value": "^curl/" }
]
}
],
"action": {
"mitigate": { "action": "challenge" }
}
}{
"name": "API Rate Limit - 100 req/min",
"active": true,
"conditionGroup": [
{
"conditions": [
{ "type": "path", "op": "pre", "value": "/api/" }
]
}
],
"action": {
"mitigate": {
"action": "rate_limit",
"rateLimit": {
"algo": "fixed_window",
"window": 60,
"limit": 100,
"keys": ["ip"],
"action": "deny"
}
}
}
}{
"name": "Login Rate Limit",
"active": true,
"conditionGroup": [
{
"conditions": [
{ "type": "path", "op": "eq", "value": "/api/auth/login" },
{ "type": "method", "op": "eq", "value": "POST" }
]
}
],
"action": {
"mitigate": {
"action": "rate_limit",
"rateLimit": {
"algo": "fixed_window",
"window": 60,
"limit": 10,
"keys": ["ip"],
"action": "challenge"
}
}
}
}| Field | Type | Description |
|---|---|---|
| string | |
| number | Seconds. Min 10, max 600 (Pro), max 3600 (Enterprise) |
| number | Max requests per window |
| array | Count per: |
| string | When exceeded: |
denyX-RateLimit-LimitX-RateLimit-Remaining{
"action": "managedRules.update",
"id": "bot_protection",
"value": { "active": true, "action": "challenge" }
}Note: The olderID is deprecated. Usebot_filterin new configurations.bot_protection
{
"action": "managedRules.update",
"id": "ai_bots",
"value": { "active": true, "action": "deny" }
}{
"name": "Allow My Monitoring Bot",
"active": true,
"conditionGroup": [
{
"conditions": [
{ "type": "user_agent", "op": "eq", "value": "MyMonitorBot/1.0" }
]
}
],
"action": {
"mitigate": { "action": "bypass" }
}
}{ "botIdEnabled": true }{
"action": "ip.insert",
"value": {
"hostname": "my-site.com",
"ip": "203.0.113.45",
"action": "deny",
"notes": "Malicious scraper"
}
}{
"action": "ip.insert",
"value": {
"hostname": "my-site.com",
"ip": "203.0.113.0/24",
"action": "deny",
"notes": "Bad actor CIDR block"
}
}{
"action": "ip.insert",
"value": {
"hostname": "my-site.com",
"ip": "198.51.100.1",
"action": "bypass",
"notes": "Internal monitoring IP"
}
}| Action | Effect |
|---|---|
| Block the IP |
| Serve JS challenge |
| Log traffic only |
| Allow through all rules (allowlist) |
hostname| ID | Protection |
|---|---|
| SQL Injection |
| Cross-Site Scripting |
| Remote Code Execution |
| Local File Inclusion |
| Remote File Inclusion |
| Scanner Detection |
| Multipart Attack |
| PHP-specific exploits |
| Generic attack patterns |
| Session Fixation |
| Java-specific exploits |
{
"action": "crs.update",
"id": "sqli",
"value": { "active": true, "action": "deny" }
}{
"firewallEnabled": true,
"crs": {
"sqli": { "active": true, "action": "deny" },
"xss": { "active": true, "action": "deny" },
"rce": { "active": true, "action": "deny" },
"lfi": { "active": true, "action": "deny" },
"rfi": { "active": true, "action": "deny" },
"sd": { "active": true, "action": "log" },
"ma": { "active": true, "action": "deny" },
"gen": { "active": true, "action": "deny" },
"sf": { "active": true, "action": "deny" },
"php": { "active": false, "action": "log" },
"java": { "active": false, "action": "log" }
},
"managedRules": {
"owasp": { "active": true, "action": "deny" },
"bot_protection": { "active": true, "action": "challenge" },
"ai_bots": { "active": true, "action": "deny" }
},
"botIdEnabled": true
}https://api.vercel.comAuthorization: Bearer <VERCEL_TOKEN>?projectId=<id>&teamId=<id>| Method | Path | Description |
|---|---|---|
| | Read current config |
| | Incremental update (add/remove/update rules) |
| | Full config replacement |
| | Create temporary bypass rule |
| Action | Description |
|---|---|
| Enable/disable firewall (value: boolean) |
| Add a custom rule |
| Update rule (requires |
| Delete rule (requires |
| Reorder rule (requires |
| Add IP rule |
| Update IP rule |
| Delete IP rule |
| Enable/configure OWASP CRS rule |
| Disable entire CRS |
| Configure managed ruleset |
curl -X PATCH "https://api.vercel.com/v1/security/firewall/config?projectId=prj_xxx&teamId=team_xxx" \
-H "Authorization: Bearer $VERCEL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"action": "rules.insert",
"value": {
"name": "Block WordPress scanners",
"active": true,
"conditionGroup": [
{
"conditions": [
{ "type": "path", "op": "re", "value": "^/wp-(admin|login|content|includes)/" }
]
}
],
"action": { "mitigate": { "action": "deny" } }
}
}'import { Vercel } from '@vercel/sdk'
const vercel = new Vercel({ bearerToken: process.env.VERCEL_TOKEN })
// Read current firewall config
const config = await vercel.security.readFirewallConfig({
configVersion: 'active',
projectId: 'prj_xxx',
teamId: 'team_xxx',
})
// Add a rule
await vercel.security.updateFirewallConfig({
projectId: 'prj_xxx',
teamId: 'team_xxx',
requestBody: {
action: 'rules.insert',
value: {
name: 'Rate limit API',
active: true,
conditionGroup: [
{ conditions: [{ type: 'path', op: 'pre', value: '/api/' }] },
],
action: {
mitigate: {
action: 'rate_limit',
rateLimit: { algo: 'fixed_window', window: 60, limit: 100, keys: ['ip'], action: 'deny' },
},
},
},
},
})curl -X POST "https://api.vercel.com/v1/security/firewall/bypass?projectId=prj_xxx&teamId=team_xxx" \
-H "Authorization: Bearer $VERCEL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"domain": "my-site.com",
"sourceIp": "198.51.100.42",
"ttl": 3600000,
"note": "Temporary bypass for load testing"
}'vercel.jsonmitigate{
"$schema": "https://openapi.vercel.sh/vercel.json",
"routes": [
{
"src": "/api/(.*)",
"missing": [
{ "type": "header", "key": "x-internal-token" }
],
"mitigate": { "action": "deny" }
},
{
"src": "/(.*)",
"has": [
{ "type": "header", "key": "user-agent", "value": "(?i)^curl/" }
],
"mitigate": { "action": "challenge" }
}
]
}vercel.json"challenge""deny"logbypasshttps://vercel.com/{team}/{project}/firewallhttps://vercel.com/{team}/{project}/firewall| Feature | Hobby | Pro | Enterprise |
|---|---|---|---|
| DDoS Protection | All | All | All |
| Custom Rules | 5 | 40 | 1000 |
| Rate Limiting | 1 rule | 40 rules | 1000 rules |
| Bot Protection (GA) | Yes | Yes | Yes |
| OWASP CRS | — | — | Yes |
| Token Bucket algo | — | — | Yes |
| Custom rate limit keys | — | — | Yes |