Setup · 2 minutes

Let Rootquill scan your site.

If a scan comes back HTTP 403 or 429, your WAF or application firewall is blocking us. Allowlist the scanner once per provider — Cloudflare, nginx, Apache, Vercel, Netlify, Caddy, or straight in your app code. Pick yours below.

Identify our scanner

User-AgentRootquill-Scanner/0.1 (+https://rootquill.com/docs/scanner)
Custom headerX-Rootquill-Scan: 1
Source IP51.81.87.112

Match on the header rather than the user-agent when possible — header matches are harder to forge and don't break if we tweak the UA later.

1 Pick your provider

Cloudflare nginx Apache Caddy Vercel Netlify Node (Express / Hono)

Cloudflare — WAF custom rule (recommended)

Takes 30 seconds in the dashboard. Works on free and paid plans.

  1. Go to your zone → Security → WAF → Custom rules → Create rule.
  2. Rule name: Allow Rootquill scanner
  3. Field: Custom header · Name: X-Rootquill-Scan · Operator: equals · Value: 1
  4. Action: Skip · tick All remaining custom rules, Browser Integrity Check, User Agent Blocking, Rate limiting, Zone lockdown, Managed Rules.
  5. Click Deploy. Live in a few seconds.

Cloudflare — via API

If you'd rather script it (or apply to 10+ zones at once):

bash
# ZONE_ID = your zone id · CF_TOKEN = a token with Zone:WAF:Edit
curl -X POST \
  -H "Authorization: Bearer $CF_TOKEN" \
  -H "Content-Type: application/json" \
  "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/rulesets/phases/http_request_firewall_custom/entrypoint" \
  -d '{
    "rules": [{
      "description": "Allow Rootquill scanner",
      "expression": "(any(http.request.headers[\"x-rootquill-scan\"][*] eq \"1\") or starts_with(http.user_agent, \"Rootquill-Scanner\"))",
      "action": "skip",
      "action_parameters": {
        "ruleset": "current",
        "products": ["bic", "hot", "rateLimit", "securityLevel", "uaBlock", "waf", "zoneLockdown"]
      },
      "enabled": true
    }]
  }'
Already done for you. Rootquill accounts that come with managed Cloudflare integration have this rule pre-applied across every zone on their account. If you added the domain to Cloudflare yourself after signing up, run the steps above.

nginx — bypass in your server block

If you've got a WAF module (ModSecurity, naxsi, etc.) or a rule set that blocks by UA, add a tiny exception at the top of the relevant server block:

nginx
server {
    listen 443 ssl;
    server_name yoursite.com;

    # Allow Rootquill scanner — must appear BEFORE any deny/limit_req / modsecurity rules
    set $rq_scan 0;
    if ($http_x_rootquill_scan = "1") { set $rq_scan 1; }
    if ($http_user_agent ~ "Rootquill-Scanner") { set $rq_scan 1; }
    if ($rq_scan = 1) {
        modsecurity off;        # if you run ModSec
        # skip whatever else blocks you — rate limiting, UA deny, etc.
    }

    # ... rest of your config ...
}

Reload with nginx -t && systemctl reload nginx.

Apache — allow via .htaccess or vhost

apache
# In your VirtualHost or .htaccess, BEFORE any deny rules:
SetEnvIfNoCase User-Agent "Rootquill-Scanner" rq_scanner
SetEnvIf X-Rootquill-Scan "1" rq_scanner

<RequireAll>
    Require all granted
    Require not env block_bots
</RequireAll>

<If "%{reqenv:rq_scanner} == 'rq_scanner'">
    UnsetEnv block_bots
</If>

If you use mod_security, add SecRuleRemoveByTag "..." conditionally on %{ENV:rq_scanner}.

Caddy — matcher + handler

caddyfile
yoursite.com {
    @rootquill_scanner {
        header X-Rootquill-Scan 1
        header_regexp User-Agent ^Rootquill-Scanner
    }

    # If you've got rate limiting or caddy-waf, skip them for @rootquill_scanner
    # Example: caddy-waf wants you to add the matcher to its exempt list.

    reverse_proxy @rootquill_scanner localhost:3000
    reverse_proxy localhost:3000  # default handler
}

Vercel — Firewall rule

  1. Open Project → Firewall → Custom Rules → Add Rule.
  2. Name: Allow Rootquill scanner
  3. If: Request Header · x-rootquill-scan · equals · 1
  4. Then: Allow. Save.

If you use middleware.ts to block traffic before the handler runs, add the same header check to bail out early:

typescript
import { NextResponse } from "next/server";

export function middleware(req) {
  // Allow Rootquill scanner through any gates below
  if (req.headers.get("x-rootquill-scan") === "1") {
    return NextResponse.next();
  }
  // ...your other middleware logic...
}

Netlify — Edge Function or _headers rule

Netlify doesn't have a WAF block-list by default, so this mostly applies if you've got a bot-protection edge function. In that function, add:

typescript
export default async (request) => {
  if (request.headers.get("x-rootquill-scan") === "1") {
    return; // let it pass through to origin
  }
  // ...your bot check...
};

Node — bypass in your bot/VPN middleware

If your app has its own IP, VPN, datacenter, or bot check (not a provider), add a one-line bypass before the block check:

javascript
// Belt-and-suspenders: require BOTH the header AND the Rootquill source IP,
// so nobody on the internet can forge the header to bypass your gate.
const ROOTQUILL_IPS = new Set(["51.81.87.112"]);

app.use((req, res, next) => {
  const isRootquill =
    req.headers["x-rootquill-scan"] === "1" &&
    ROOTQUILL_IPS.has(req.ip);
  if (isRootquill) return next(); // skip VPN/bot gate
  // ...your existing VPN / bot check...
});

Hono is the same shape — c.req.header('x-rootquill-scan') and c.env.CF_CONNECTING_IP (or your framework's equivalent).

Trust the IP, not just the header. If your gate accepts the header from anywhere, anyone on the internet can bypass it by sending X-Rootquill-Scan: 1. Always pair the header check with the source IP above.

2 Verify

Re-scan from the dashboard

Open Dashboard → Scanner, paste your URL, and click Scan. A successful response shows the issues ranked by impact. If you still get 403 / 429, the block is happening before the layer you configured — work out which hop is blocking (origin? CDN? edge function?) and apply the rule there too.

3 After launch