---
title: "Protect Sensitive Routes with Vercel WAF: Challenge and Deny Rule Recipes"
description: Use Vercel WAF custom rules to block or challenge unwanted traffic by country, ASN, IP address, user agent, path, or cookie, without redeploying.
url: /kb/guide/suspicious-traffic-in-specific-countries
canonical_url: "https://vercel.com/kb/guide/suspicious-traffic-in-specific-countries"
published: 2025-11-03
last_updated: 2026-09-03
authors: DX Team
related:
  - /docs/vercel-firewall/vercel-waf
  - /docs/vercel-firewall/vercel-waf/rule-configuration
  - /docs/rest-api/security/update-firewall-configuration
  - /docs/vercel-firewall/vercel-waf/managed-rulesets
  - /docs/vercel-firewall/vercel-waf/ip-blocking
  - /kb/guide/traffic-spikes
  - /docs/vercel-firewall/vercel-waf/system-bypass-rules
  - /blog/understanding-csrf-attacks
  - /docs/vercel-firewall/vercel-waf/custom-rules
  - /docs/vercel-firewall/vercel-waf/examples
  - /docs/vercel-firewall/firewall-concepts
  - /kb/guide/limit-abuse-with-rate-limiting
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---
<!-- docsgraph:related -->
## Related pages

> **For AI agents:** Follow these links to understand how this page connects to the rest of the Vercel ecosystem. For the full cross-link map (inbound, outbound, prerequisites, and semantic neighbors), see the .graph.md link below.

- [vercel firewall](https://vercel.com/docs/cli/firewall?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related) — Learn how to manage your project's custom firewall rules, IP blocks, system bypass rules, attack challenge mode, and sys
- [WAF Rate Limiting](https://vercel.com/docs/vercel-firewall/vercel-waf/rate-limiting?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related) — Learn how to configure custom rate limiting rules with the Vercel Web Application Firewall \\(WAF\\).
- [Vercel WAF upgrade brings persistent actions, rate limiting, and API control](https://vercel.com/blog/vercel-waf-upgrade-brings-persistent-actions-rate-limiting-and-api-control?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related)
- [Block, rate limit, and challenge traffic with the Vercel Firewall](https://vercel.com/changelog/block-rate-limit-and-challenge-traffic-with-the-vercel-firewall?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related)
- [Deny non-browser traffic or blocklisted ASNs](https://vercel.com/kb/guide/deny-non-browser-traffic-or-blocklisted-asns?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related) — Learn how to block traffic from known threats with the Vercel WAF API.
- [Challenge cookie-less requests on a specific path](https://vercel.com/kb/guide/challenge-cookieless-requests-on-a-specific-path?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related) — Learn how to challenge specific requests with the Vercel WAF API.
- [How to Utilize Vercel’s Bot Management Features](https://vercel.com/kb/guide/how-to-utilize-vercels-bot-management-features?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=related) — A practical, step-by-step guide to identifying unwanted automated traffic and securing your Vercel apps with Bot Protect

Full cross-link map for this page: [/kb/guide/suspicious-traffic-in-specific-countries.graph.md](/kb/guide/suspicious-traffic-in-specific-countries.graph.md?from=related&source_path=%2Fkb%2Fguide%2Fsuspicious-traffic-in-specific-countries&source_site=vercel-kb&relationship=graph)
<!-- /docsgraph:related -->


## How to block or challenge unwanted traffic with Vercel WAF custom rules

The live monitor on your project's Firewall page is where unwanted traffic shows up first. A spike from a country, network, or client you weren't expecting is usually the sign that something needs a rule. The [Vercel Web Application Firewall](https://vercel.com/docs/vercel-firewall/vercel-waf) (WAF) gives you that rule, and applying it takes effect globally within 300ms without a new deployment.

Here's how the rules engine works, followed by the rules teams set up first and the legitimate traffic each one can block by accident.

## What can Vercel WAF custom rules do?

A custom rule compares a parameter from the incoming request against a value you set, then applies an action when the conditions match. You can combine conditions with **AND** or **OR**, and you can reorder rules so the ones you want evaluated first run first.

Parameters fall into three groups. Request properties include path, route, HTTP method, user agent, header, query, cookie, hostname, and protocol. Location conditions match on continent, country, state, city, IP address, and Autonomous System Number (ASN). Client fingerprints use the JA3 and JA4 TLS digests.

The operator decides how that parameter is compared to your value, with choices including exact matches, list membership, contains, starts with, ends with, and Perl Compatible Regular Expressions (PCRE). Any operator can be negated, so a condition can read as user agent does not contain `Mozilla`.

Each rule ends in one of five actions:

- **Log:** Records the match and lets the request through untouched. Rule evaluation continues afterward, so a later rule can still act on the request.
  
- **Deny:** Returns a `403 Forbidden` and stops evaluating further rules. The request never reaches your application.
  
- **Challenge:** Serves the Vercel Security Checkpoint, which a client passes only by executing JavaScript. Real browsers solve it once and continue, while scripts cannot.
  
- **Bypass:** Skips every remaining custom rule and lets the request through. This is how you exempt trusted automation from a rule that would otherwise catch it.
  
- **Redirect:** Sends a temporary `307` to a path you choose and stops evaluating further rules. Matched traffic lands somewhere you control instead of being blocked.
  

Deny and challenge are the two actions you'll choose between. Deny fits traffic that no human would send, such as vulnerability scans. Challenge fits a condition that a real visitor might also meet, and a browser that passes gets a 1-hour session before it has to solve the check again. Because both are cheap to get wrong, every rule in this guide starts in **Log** mode so you can watch the match before you enforce it.

## How to block traffic from specific countries in the Vercel WAF

Geographic rules are the most common starting point, usually after a spike appears from a region you don't serve. They also cover the reverse case, where sanctions or data protection rules require you to turn a region off.

To see the current split, open the Firewall overview page. Select **Allowed Requests** from the traffic grouping drop-down, then **Country** for the secondary drop-down.

Once you know which country you want to act on, create the rule:

1. From your [dashboard](https://vercel.com/dashboard), select your project, then open **Firewall** in the sidebar.
   
2. Select **⋯** > **Configure** on the top right of the Firewall overview page.
   
3. Select **Add New...** > **Rule**.
   
4. Enter a name such as `Ireland traffic` and a description such as `Understand Ireland traffic spike`.
   
5. In the **Configure** section, set the **If** condition to **Country** **Equals**, then select the country in the third drop-down.
   
6. Select **Log** for the **Then** action, then select **Save Rule**.
   
7. Select **Review Changes**, review what will be applied, then select **Publish**.
   
8. Watch the rule'’ traffic on the Firewall overview page until you're satisfied the match is right.
   
9. Return to the rule, change **Then** to **Deny**, save, and publish again.
   

Country is derived from the client IP address, so the rule follows the network the visitor is on rather than where they live. Visitors on a VPN, traveling, or behind a corporate egress in another region will match too. That's the main reason to sit in **Log** mode long enough to see a full traffic cycle before denying.

## How to deny non-browser traffic or blocklisted ASNs with the Vercel WAF API

Country rules act on where a request comes from. A user agent condition acts on the client software making the request, and a network condition acts on the ASN it connects from. One rule can carry both.

Real browsers all report a `Mozilla` token, so matching user agents that lack one catches bots and scrapers that never bothered to spoof it. An [ASN condition](https://vercel.com/docs/vercel-firewall/vercel-waf/rule-configuration) turns off entire networks tied to malicious activity. The field accepts digits only.

The following route sends a `PATCH` request to the [Update Firewall Configuration](https://vercel.com/docs/rest-api/security/update-firewall-configuration) endpoint to insert that rule, with the two conditions in separate condition groups so either one can match:

```tsx
export async function PATCH() {
  let baseUrl = '<https://api.vercel.com/v1/security/firewall/config>';
  let teamId = 'team_a5j...';
  let projectId = 'QmTrK...';

  const body = JSON.stringify({
    action: 'rules.insert',
    id: null,
    value: {
      active: true /** Whether this rule is enabled in your Vercel WAF configuration */,
      name: 'Deny non-browser traffic or blocklisted ASN',
      description: 'Deny traffic without Mozilla or from a specific ASN',
      conditionGroup: [
        /** Any of the condition groups in this array can match */
        {
          conditions: [
            {
              neg: true /** Perform negative match */,
              op: 're' /** Matches expression */,
              type: 'user_agent' /** Parameter from incoming traffic */,
              value: '.*Mozilla.*',
            },
          ],
        },
        {
          conditions: [
            {
              op: 'inc' /** Is any of, matching any value in the array */,
              type: 'geo_as_number' /** Parameter from incoming traffic */,
              value: ['124', '456', '789'],
            },
          ],
        },
      ],
      action: {
        mitigate: {
          action: 'deny',
          rateLimit: null,
          redirect: null,
          actionDuration: null,
        },
      },
    },
  });

  let res = await fetch(`${baseUrl}?projectId=${projectId}&teamId=${teamId}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body,
  });

  if (!res.ok) {
    return Response.json(
      { status: 'Failed to update Firewall' },
      { status: res.status },
    );
  }

  return Response.json({ status: 'New rule added to Firewall' });
}
```

Both `conditionGroup` and `action` are required. A condition this broad also matches uptime monitors, CI jobs, your own API clients, and search crawlers. Set the action to `log` first and read what it catches. If your goal is bot traffic rather than a specific network, the [Bot Protection managed ruleset](https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets#configure-bot-protection-managed-ruleset) excludes verified bots such as Google's crawler automatically.

## How to deny traffic from a set of IP addresses with a WAF IP blocking rule

User agents and ASNs cover broad patterns. Once you've identified the exact addresses behind the traffic, an IP blocking rule is more precise, and it applies across every domain on the project at once. It fits an IP network tied to a DDoS attack or automated bot traffic, and any network you're required to block under sanctions or data protection law.

The following route inserts a project-level [IP blocking rule](https://vercel.com/docs/vercel-firewall/vercel-waf/ip-blocking#project-level-ip-blocking) for a `/24` range across all hostnames:

```tsx
export async function PATCH() {
  let baseUrl = '<https://api.vercel.com/v1/security/firewall/config>';
  let teamId = 'team_a5j...';
  let projectId = 'QmTrK...';

  const body = JSON.stringify({
    action: 'ip.insert',
    id: null,
    value: {
      action: 'deny',
      hostname: '*' /** Applies to every domain on the project */,
      ip: '12.34.56.0/24',
      notes: 'deny traffic from 12.34.56.0/24',
    },
  });

  let res = await fetch(`${baseUrl}?projectId=${projectId}&teamId=${teamId}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body,
  });

  if (!res.ok) {
    return Response.json(
      { status: 'Failed to update Firewall' },
      { status: res.status },
    );
  }

  return Response.json({ status: 'New rule added to Firewall' });
}
```

Project-level IP blocking allows up to 3 rules on Hobby, 100 on Pro, and 1000 on Enterprise. Ranges are shared infrastructure. A `/24` can cover a corporate gateway or a mobile carrier alongside the address you're targeting, so narrowing the CIDR is safer than widening it. To do the same for one address from the dashboard, see [blocking a single IP](https://vercel.com/kb/guide/traffic-spikes).

## How to challenge cURL requests with a WAF custom rule

Denying an address only works once you know it. Challenges work on clients you can't enumerate, because they test capability rather than identity.

cURL (Client URL) is a common vehicle for scraping, brute force attempts, and other automated activity. A challenge is the right action here rather than a deny, since a real browser solves it once and continues while the script cannot.

The following route inserts a rule that challenges any request whose user agent contains `curl`:

```tsx
export async function PATCH() {
  let baseUrl = '<https://api.vercel.com/v1/security/firewall/config>';
  let teamId = 'team_a5j...';
  let projectId = 'QmTrK...';

  const body = JSON.stringify({
    action: 'rules.insert',
    id: null,
    value: {
      active: true /** Whether this rule is enabled in your Vercel WAF configuration */,
      name: 'Challenge Curl',
      description: 'Challenge all traffic from curl requests',
      conditionGroup: [
        {
          conditions: [
            {
              op: 'sub' /** Contains */,
              type: 'user_agent' /** Parameter from incoming traffic */,
              value: 'curl',
            },
          ],
        },
      ],
      action: {
        mitigate: {
          action: 'challenge',
          rateLimit: null,
          redirect: null,
          actionDuration: null,
        },
      },
    },
  });

  let res = await fetch(`${baseUrl}?projectId=${projectId}&teamId=${teamId}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body,
  });

  if (!res.ok) {
    return Response.json(
      { status: 'Failed to update Firewall' },
      { status: res.status },
    );
  }

  return Response.json({ status: 'New rule added to Firewall' });
}
```

Automated tools can't establish a challenge session. That's the point of the rule and also its cost. Legitimate command-line clients, deploy scripts, and partner integrations hitting the same paths will start failing. Give those sources a [system bypass rule](https://vercel.com/docs/vercel-firewall/vercel-waf/system-bypass-rules) or a custom rule with the `bypass` action before you enforce.

## How to block PHP requests with a Vercel WAF rule

Scanners look for the same handful of paths on every site they touch, and `.php` is near the top of the list. If your application doesn't serve PHP, a path rule turns that entire class of probe into a `403`.

Blocking these requests helps in a few ways:

- **Vulnerability scanning:** Attackers scan for PHP files to find exploitable versions or misconfigurations, and a deny rule ends the scan at the first request.
  
- **Log noise:** Probe traffic you don't serve makes real errors harder to spot, so removing it improves what's left.
  
- **Exploitation attempts:** Automated attacks target WordPress, phpMyAdmin, and other PHP applications, none of which exist on a project that doesn't use PHP.
  

Because the condition is a path match rather than a signature, one rule covers every variant a scanner tries.

Create the rule from the dashboard:

1. From your [dashboard](https://vercel.com/dashboard), select your project, then open **Firewall** in the sidebar.
   
2. Select **⋯** > **Configure** on the top right of the Firewall overview page.
   
3. Select **Add New...** > **Rule**.
   
4. Enter `Block .php request paths` as the name and `Blocks any request containing .php` as the description.
   
5. In the **Configure** section, set the **If** condition to **Request Path** **Contains**, then type `.php` in the input box.
   
6. Select **Log** for the **Then** action, then select **Save Rule**.
   
7. Select **Review Changes**, review what will be applied, then select **Publish**.
   
8. Watch the rule's traffic on the Firewall overview page to confirm it only matches probes.
   
9. Return to the rule, change **Then** to **Deny**, save, and publish again.
   

The condition matches anywhere in the path, so a route such as `/blog/from-php-to-node` matches too. Check the logged traffic for your own URLs before switching to **Deny**, and tighten the condition to **Ends with** if you need to.

## How to challenge cookieless requests on a specific path with the WAF API

Path and user agent rules act on the request surface. A cookie condition goes one step further and acts on session state, protecting an authenticated area from requests your own front end never issued.

A request to a sensitive path with no session cookie rarely comes from your own front end. Challenging it prevents unauthorized access and adds a layer against [CSRF attacks](https://vercel.com/blog/understanding-csrf-attacks), short for Cross-Site Request Forgery.

The following route inserts a rule that challenges requests starting with `/api` when they don't carry a `_session` cookie, using two conditions in the same group so both must match:

```tsx
export async function PATCH() {
  let baseUrl = '<https://api.vercel.com/v1/security/firewall/config>';
  let teamId = 'team_a5j...';
  let projectId = 'QmTrK...';

  const body = JSON.stringify({
    action: 'rules.insert',
    id: null,
    value: {
      active: true /** Whether this rule is enabled in your Vercel WAF configuration */,
      name: 'Challenge Cookieless requests',
      description: 'Challenge all traffic without session cookies on a specific path',
      conditionGroup: [
        {
          conditions: [
            /** Both conditions need to match */
            {
              op: 'pre' /** Starts with */,
              type: 'path' /** Parameter from incoming traffic */,
              value: '/api',
            },
            {
              neg: true /** Perform negative match, giving "does not contain" */,
              op: 'sub' /** Contains */,
              type: 'cookie' /** Parameter from incoming traffic */,
              value: '_session',
            },
          ],
        },
      ],
      action: {
        mitigate: {
          action: 'challenge',
          rateLimit: null,
          redirect: null,
          actionDuration: null,
        },
      },
    },
  });

  let res = await fetch(`${baseUrl}?projectId=${projectId}&teamId=${teamId}`, {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body,
  });

  if (!res.ok) {
    return Response.json(
      { status: 'Failed to update Firewall' },
      { status: res.status },
    );
  }

  return Response.json({ status: 'New rule added to Firewall' });
}
```

Requests your application makes inside a valid challenge session succeed automatically, so first-party calls from a browser that has already solved the challenge are unaffected. Webhooks, mobile clients, and server-to-server calls to `/api` are a different matter, since they can't establish a session. Give them a `bypass` rule, or scope the path condition to the routes that require a session.

## How to test Vercel WAF rules before you enforce them

Each of these rules lands in the same place, a condition you believe matches only unwanted traffic. Testing is how you find out whether that belief is correct, while the cost of being wrong is still zero.

Follow the same sequence each time:

1. Create the rule with a **Log** action and publish it.
   
2. Watch the live traffic view on the Firewall overview page, grouping by the parameter your condition uses.
   
3. Adjust the condition until the matches contain only traffic you're willing to block.
   
4. Change the action to **Challenge**, **Deny**, or **Bypass**, then publish again.
   

Two features make the switch safer. [Persistent actions](https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules#persistent-actions) add a time-based block to a challenge or deny action. A client that trips the rule then stays blocked at the platform-wide firewall for a window you choose. And if a rule misbehaves once live, open **View Audit Log** from the ellipsis menu on the Firewall page and restore an earlier configuration.

## Next steps

Once a rule matches the traffic you want, the same test-then-enforce sequence applies to every other condition the WAF supports. [Browse more WAF examples](https://vercel.com/docs/vercel-firewall/vercel-waf/examples) for rules built around headers, methods, and TLS fingerprints, or [start a new project](https://vercel.com/new) to configure the firewall on a fresh deployment.

## Related resources

- [WAF Custom Rules](https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules)
  
- [Rule Configuration Reference](https://vercel.com/docs/vercel-firewall/vercel-waf/rule-configuration)
  
- [Firewall concepts](https://vercel.com/docs/vercel-firewall/firewall-concepts)
  
- [WAF IP Blocking](https://vercel.com/docs/vercel-firewall/vercel-waf/ip-blocking)
  
- [Blocking traffic from a specific IP address](https://vercel.com/kb/guide/traffic-spikes)
  
- [Limit abuse with rate limiting](https://vercel.com/kb/guide/limit-abuse-with-rate-limiting)