GitHub

Debug Cloudflare 403 Blocks

Step-by-step guide for diagnosing why Cloudflare is returning a 403 for an API request. You will need the Ray ID from the blocked response.

Prerequisites

  • Access to the Cloudflare Dashboard
  • The Ray ID from the 403 response (found in the cf-ray response header or on the block page itself)

Where to Find the Ray ID

The Ray ID appears in two places:

  1. Response header: cf-ray: 8a1b2c3d4e5f6g7h-DFW (the suffix is the Cloudflare data center)
  2. Block page HTML: displayed at the bottom of Cloudflare’s default 403 page

If the caller is an API client, capture the cf-ray header from the response. Example with curl:

curl -v https://api.example.com/endpoint 2>&1 | grep -i cf-ray

Step 1: Look Up the Ray ID in Security Events

  1. Log in to Cloudflare Dashboard
  2. Select the appropriate zone (domain)
  3. Go to Security > Events (under the Security section in the left sidebar)
  4. In the filter bar, add a filter: Ray ID equals <your-ray-id>
  5. Click the matching event to expand the details

This tells you exactly what blocked the request and why.

Step 2: Identify What Blocked the Request

The event detail shows a Service and Rule that triggered the block. Common sources:

Service What It Means
Security Rules (Managed) Cloudflare Managed Ruleset or OWASP Core Ruleset flagged something in the request
Security Rules (Custom) A custom security rule written by your team matched the request
Rate Limiting Request exceeded a rate limit threshold
Bot Fight Mode Cloudflare classified the caller as bot traffic
Cloudflare Access The request failed an Access policy (missing/invalid JWT, wrong identity provider, IP not in policy)
IP Access Rules The source IP is on a block list
Hotlink Protection Hotlink protection is interfering with API calls (should not be enabled on API paths)

Record the Rule ID, Rule name, and Action (Block, Challenge, JS Challenge, etc.).

Step 3: Inspect the Request Details

The event also shows the full request that was evaluated:

  • Source IP and ASN
  • Country
  • URI path and query string
  • HTTP method
  • User-Agent
  • Request headers

Compare these against what you expect. Common issues:

  • Missing or generic User-Agent (triggers bot detection)
  • Request body containing patterns that look like SQL injection or XSS to the managed ruleset (common with JSON payloads that include code snippets, HTML, or special characters)
  • IP address from an unexpected region or cloud provider ASN
  • Missing Cloudflare Access JWT (cf-access-token cookie or CF-Access-Client-Id/CF-Access-Client-Secret headers)

Step 4: Fix the Block

If Security Rules (Managed Rules) blocked it

Managed rules live under Security > Security rules > Managed rules in the Cloudflare dashboard. THB currently has two active managed rulesets:

  1. Cloudflare Managed Ruleset (order 1) — matches all incoming requests
  2. Cloudflare OWASP Core Ruleset (order 2) — may have path exclusions configured (e.g., specific URI paths skipped via the “Match against” filter)

To investigate and fix:

  1. Note the Rule ID from the Security Event
  2. Go to Security > Security rules and select the Managed rules tab
  3. Click the ruleset name (e.g., “Cloudflare Managed Ruleset”) to see which individual rules are active
  4. Search for the Rule ID to find the specific rule that triggered

To add a path exclusion to a managed ruleset:

  1. Click the three-dot menu on the ruleset row
  2. Select Edit
  3. In the Match against filter, add a condition to exclude the affected path. For example, if the OWASP ruleset is blocking /ProviderDocuments/CreateAjax, add: URI Path does not equal /ProviderDocuments/CreateAjax
  4. Save the rule

To create an exception (skip a specific rule):

  1. On the Security rules page, click Create rule
  2. Define a narrow match expression, for example:
    • URI Path contains /api/v1/specific-endpoint
    • AND Source IP is in <your-known-IP-range>
  3. Set the action to Skip and select which managed rule(s) to skip
  4. Save and deploy

Other options (use sparingly):

  • Set the rule to Log instead of Block while you investigate (click into the ruleset, find the rule, change its action)
  • Disable the rule entirely (last resort, reduces security posture)

If a Custom Security Rule blocked it

Custom rules are also under Security > Security rules. Switch to the custom rules view (or look for rules not part of a managed ruleset).

  1. Find the rule by its name or ID from the Security Event
  2. Review the rule expression (it might be overly broad, e.g., blocking all non-browser User-Agents)
  3. Click the rule to edit its expression
  4. Adjust the expression to exclude the legitimate API traffic, or add an exception for the specific path/IP
  5. Save and deploy

If Rate Limiting blocked it

  1. Go to Security > Security rules and use the rule-type dropdown (defaults to Managed rules) or Show all rule types to find Rate limiting rules
  2. Find the matching rule
  3. Check the threshold (requests per period) and whether it’s appropriate for the API’s expected traffic volume
  4. Adjust the threshold, or add an exception for the caller’s IP/path

If Bot Fight Mode blocked it

Bot Fight Mode is designed for browser-facing sites and aggressively blocks automated traffic. It should not be enabled for API endpoints.

  1. Go to Security > Settings and find the Bots section (Bot Fight Mode lives here, not under a separate WAF/Bots menu)
  2. Check if Bot Fight Mode or Super Bot Fight Mode is enabled
  3. If the zone serves both browser and API traffic, create a custom security rule to skip bot checks on API paths:
    • Go to Security > Security rules
    • Create a rule: URI Path starts with /api/ with action Skip and select Bot Fight Mode

If Cloudflare Access blocked it

THB uses Cloudflare Access with tunnels for internal services. A 403 from Access means the request failed the Access policy.

  1. Go to Access > Applications
  2. Find the application matching the blocked hostname
  3. Check the policies to understand who/what is allowed
  4. Common causes:
    • Missing service token headers: API clients authenticating via service tokens must include CF-Access-Client-Id and CF-Access-Client-Secret headers
    • Expired service token: check the token’s expiration in Access > Service Auth > Service Tokens
    • IP not in policy: if the policy restricts by IP, confirm the caller’s egress IP is included
    • Wrong identity provider: the user authenticated with a provider not allowed by the policy

For service-to-service calls through Cloudflare tunnels, verify the cloudflared service is running and the tunnel is healthy:

# On the origin VM
docker service ls | grep cloudflared
docker service logs --tail 50 <cloudflared-service-name>

If IP Access Rules blocked it

  1. Go to Security > Security rules, then use the rule-type dropdown or Show all rule types to find IP access rules
  2. Search for the blocked IP
  3. If it’s legitimately blocked, coordinate with whoever added the rule
  4. If it was added in error, remove or change the action to Allow

Step 5: Verify the Fix

After making changes, have the caller retry the request and confirm:

  1. The response is no longer a 403
  2. The cf-ray header is still present (confirms traffic is still going through Cloudflare)
  3. Check Security > Events again to verify no new blocks

Quick Reference: Cloudflare Access Service Tokens

For API clients that need to authenticate through Cloudflare Access without browser-based login:

curl -H "CF-Access-Client-Id: <client-id>" \
     -H "CF-Access-Client-Secret: <client-secret>" \
     https://internal-app.thehelperbees.com/api/endpoint

Service tokens are managed in Access > Service Auth > Service Tokens. They have configurable expiration and can be rotated without downtime by creating a new token before revoking the old one.

Not Cloudflare?

If the Ray ID returns no results in Security Events, the 403 may not be from Cloudflare:

  • Check the response headers. A real Cloudflare block will have server: cloudflare and a cf-ray header. If these are absent, the 403 is coming from the origin application or reverse proxy (Caddy).
  • Wrong zone. Verify which Cloudflare zone the domain belongs to. You may be searching the wrong one.
  • Check Caddy logs on the origin VM. Caddy sits between cloudflared and the application and can return its own 403s.
Edit this page