# BitNinja WebShield Blocks Royal MCP OAuth Discovery

> BitNinja WebShield returns a JavaScript CAPTCHA for /.well-known/ and /wp-json/ on cPanel hosts, blocking Royal MCP clients. Ask your host to exempt them.

- Canonical: <https://royalplugins.com/support/royal-mcp/bitninja-webshield-blocks-mcp/>
- HTML version: <https://royalplugins.com/support/royal-mcp/bitninja-webshield-blocks-mcp/>

---

If Claude, ChatGPT, or Cursor fails to connect to your Royal MCP endpoint and a direct curl against `/.well-known/oauth-authorization-server` returns an HTML page titled “One moment, please...” with an auto-reload script, your host is running BitNinja WebShield in front of WordPress. WebShield serves a JavaScript CAPTCHA challenge for these paths — MCP clients don’t execute JavaScript, so the OAuth handshake dies at discovery. Fix is at the hosting layer; no WordPress setting will resolve it.

Before applying this fix

Royal MCP connection issues that look host-specific are often a plugin conflict, cache layer, or stale OAuth state — work through our [**Troubleshooting Start Here**](https://royalplugins.com/support/royal-mcp/troubleshooting-start-here/) checklist first if you haven’t already. This article assumes the basics are ruled out and the curl fingerprint below matches.

### Often stacked on top of Imunify360

BitNinja WebShield and Imunify360 are both host-layer bot-protection products, and shared cPanel/CloudLinux hosts frequently run both. If your OAuth endpoints show HTTP 200 with an `“Access denied by Imunify360”` JSON body *and* `/.well-known/\*` paths return the “One moment, please...” HTML page, you have both layers to clear — see the [Imunify360 fix](https://royalplugins.com/support/royal-mcp/imunify360-blocks-mcp/) for the companion request.

## Symptoms

You’re hitting this specific issue if the following are true:

#### The Telltale Signs

- Claude.ai, ChatGPT, or Cursor fails to connect to your Royal MCP endpoint — often silently or with a reference code the client can’t explain
- A `curl` against `/.well-known/oauth-authorization-server` returns HTTP 200 with a `Content-Type: text/html` body containing `<title>One moment, please...</title>` and an auto-reload `setTimeout(function(){window.location.reload();}, 5000)`
- The HTML body includes obfuscated JavaScript that references `wsidchk` (a form parameter) and `webdriverCheck` (a bot-fingerprint function name)
- Reproduces on ALL User-Agents — browser UAs, plain `curl`, `python-httpx` — the challenge fires unconditionally on the path
- Royal MCP’s Activity Log (WP Admin → Royal MCP → Activity Log) stays empty for every failed connection attempt — the requests never reach WordPress
- Your hosting provider is a shared cPanel host running CloudLinux — often layered on top of Imunify360

Why MCP clients can’t solve the challenge

BitNinja WebShield’s JavaScript CAPTCHA relies on a real browser executing the challenge script, setting a clearance cookie, and reloading the page. MCP clients (Claude, ChatGPT, Cursor) are headless HTTP clients — they don’t run a JavaScript engine, don’t maintain a browser cookie jar for challenge responses, and don’t reload. The response body they receive is HTML where they expected JSON, so the OAuth handshake aborts at discovery.

## How to Confirm It’s BitNinja WebShield

A 30-second curl test confirms the signature. Run from any terminal — replace `example.com` with your actual domain:

#### Test your OAuth discovery URL

```
curl -i https://example.com/.well-known/oauth-authorization-server
```

The diagnostic fingerprint is **HTTP 200** with `Content-Type: text/html` and a response body starting with:

```
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <script>
      (function(){
          setTimeout(function(){
              window.location.reload();
          }, 5000);
      }())
  </script>
  <link rel="icon" href="data:,">
  <title>One moment, please...</title>
```

#### Grep the response body for BitNinja’s signature tokens

```
curl -s https://example.com/.well-known/oauth-authorization-server | grep -oE "wsidchk|webdriverCheck"
```

If both `wsidchk` and `webdriverCheck` appear in the output, you’re looking at BitNinja WebShield’s JS challenge. These tokens only co-occur in WebShield’s obfuscated fingerprinting script.

#### Confirm the block is path-based, not domain-wide

```
curl -I https://example.com/
```

Your site homepage should return HTTP 200 with normal WordPress output (no interstitial). This proves the intercept is scoped to specific paths — the rest of the site works normally.

Why the Activity Log stays empty

BitNinja WebShield sits in front of WordPress at the web-server layer, so denied requests never reach PHP and never register in the plugin’s Activity Log. An empty log combined with a Claude connection failure isn’t proof of WebShield by itself — but combined with the curl fingerprint above, it’s conclusive.

## The Fix — Ask Your Host to Disable WebShield for the OAuth Paths

BitNinja WebShield runs at the hosting provider’s layer, not inside WordPress. The fix is a support ticket to your host asking them to exclude specific paths from WebShield (or disable WebShield for the whole domain). Most hosts complete this in under 24 hours — it’s a routine change on their end.

### Ready-to-paste hosting-provider request

Copy the text below into a support ticket with your hosting provider. Substitute `example.com` with your actual domain:

```
Subject: Please exclude OAuth paths from BitNinja WebShield for example.com

Hi,

We’re running the Royal MCP WordPress plugin on example.com,
which lets AI assistants like Claude, ChatGPT, and Cursor
connect to our site via the Model Context Protocol (MCP).
The connection requires OAuth 2.0 discovery and token endpoints
to be reachable without a JavaScript challenge.

Right now BitNinja WebShield is intercepting these paths and
returning an HTML CAPTCHA page (title: “One moment, please...”)
with the wsidchk / webdriverCheck challenge script. MCP clients
don’t execute JavaScript, so the OAuth handshake dies at
discovery.

Please exclude the following URL paths for example.com from
BitNinja WebShield (or, equivalently, disable WebShield for the
whole domain):

  /wp-json/*
  /.well-known/*
  /authorize
  /token
  /register

Important: please use a path-based or domain-level exclusion,
NOT an IP allowlist. Claude.ai and other MCP client backends
connect from constantly-rotating IP ranges, so an IP-based rule
will silently break within a few weeks. Path or domain scope
is the correct mechanism.

These are standard WordPress REST API + OAuth 2.0 (RFC 8414)
endpoints. Excluding them from WebShield will not reduce
security — they are the plugin’s own authenticated
endpoints subject to WordPress’s standard capability checks.

Thanks in advance.
```

If your host asks “why can’t you just whitelist your IP?”

Claude.ai and Anthropic’s backend connect from constantly-rotating IP ranges. You can’t whitelist a fixed set of Anthropic IPs, so path-based exclusion is the correct approach. The paste-ready text above already explains this — point your host at this article if they need further convincing.

### Retry the connection

Once your host confirms the exclusion is in place, the fix is immediate:

1. Re-run the curl test from the “How to Confirm” section above. You should now see a normal OAuth metadata JSON response, not the HTML challenge page.
2. In your MCP client, delete the existing Royal MCP connector if present, wait 30 seconds, then re-add it fresh. This forces the full OAuth flow from scratch rather than reusing cached failure state.
3. Complete the authorize consent screen. Royal MCP’s Activity Log should now show entries the moment you click “Authorize.”

## What NOT to Try (WordPress-Side Fixes Won’t Help)

BitNinja WebShield runs at the web-server layer, ahead of PHP. That means the following common troubleshooting steps will NOT resolve this issue — don’t waste time on them:

- **Changing Royal MCP settings.** No plugin setting can affect requests that never reach PHP. Same for other security or cache plugins.
- **Deactivating other plugins.** If WebShield is the block, no combination of active/inactive WordPress plugins will change the outcome.
- **Adding `.htaccess` rewrite rules.** Apache/LiteSpeed process `.htaccess` AFTER WebShield has already served the challenge. Rules won’t fire.
- **Switching to a default theme.** Themes only render pages that WordPress successfully processes — irrelevant here.
- **Whitelisting IPs in WordPress security plugins.** Those plugins don’t see the request either. Only WebShield’s exclusion matters.
- **Asking your host for an IP allowlist.** Anthropic’s outbound IPs rotate; an IP-based rule silently breaks within weeks. Insist on a path-based or domain-level exclusion.
- **Uninstalling & reinstalling Royal MCP.** The plugin is fine — the problem is upstream of WordPress.

## What Royal MCP 1.4.39 Already Does for You

Royal MCP 1.4.39 detects this exact failure mode automatically. When the plugin’s well-known-endpoint self-check sees an HTML response containing both `wsidchk` and `webdriverCheck`, it surfaces a dismissible admin notice on the Plugins screen and Royal MCP settings linking back to this page.

So future customers on hosts running BitNinja WebShield will land here from inside their own WordPress admin instead of having to search for “Royal MCP can’t connect” and stumble across the diagnosis. The plugin can’t auto-fix the block for you — WebShield is upstream of WordPress and the customer’s hosting-provider exclusion is the only mechanism — but the notice makes the diagnosis fast.

A caveat about the self-check

Royal MCP’s well-known probe runs from the site to itself (a WordPress-loopback HTTP request). Some WebShield configurations exclude loopback traffic and some don’t. If loopback is excluded but external traffic still gets the challenge, our self-check will see a valid metadata response and NOT trigger the notice — even though Claude (external) still can’t connect. If you see this pattern, run the curl tests in the “How to Confirm” section above from EXTERNAL to your site — those are the definitive diagnostic.

## Related Issues You Might Also Be Hitting

BitNinja WebShield is often deployed alongside other hosting-layer bot-protection products on shared cPanel + CloudLinux hosts. Check for these companion cases:

#### Imunify360 blocks OAuth (HTTP 200 + JSON denial)

Common companion to WebShield on the same hosts. Fingerprint is HTTP 200 with a JSON body: `{"message":"Access denied by Imunify360 bot-protection..."}` instead of the WebShield HTML challenge.

[**See: Imunify360 fix →**](https://royalplugins.com/support/royal-mcp/imunify360-blocks-mcp/)

#### Apache python-httpx 429

Different fingerprint from the WebShield HTML challenge. HTTP 429 with a plain “Too Many Requests” body specifically for User-Agent `python-httpx/*`. Anthropic’s OAuth backend uses that UA.

[**See: Apache python-httpx 429 fix →**](https://royalplugins.com/support/royal-mcp/apache-python-httpx-429-token-exchange/)

#### ModSecurity 406 bot-fingerprint

Similar UA-fingerprinting failure mode but from a different security layer — cPanel ModSecurity returns HTTP 406 with “Mod Security” visible in the response body.

[**See: ModSecurity 406 fix →**](https://royalplugins.com/support/royal-mcp/modsecurity-406-bot-fingerprint/)

## Still Stuck? Two Support Paths

If your host completed the allowlist and Claude still can’t connect:

### Community Support (free) — wp.org Plugin Forum

Post a new thread at [wordpress.org/support/plugin/royal-mcp/](https://wordpress.org/support/plugin/royal-mcp/). Include the diagnostic info listed below in your post.

### Premium Support (paid) — direct one-on-one help

For priority response (24-hour SLA) and hands-on diagnostic help, our [Premium Support tier](https://royalplugins.com/premium-support/) is $149/year.

#### Information to include in your post or ticket

- **Full curl output** for `/wp-json/royal-mcp/v1/mcp` and `/.well-known/oauth-authorization-server` BEFORE and AFTER your host applied the allowlist (use `curl -i`, not just `-I`, so we see the body)
- **Your hosting provider** (SiteGround, Bluehost, HostGator, Namecheap, etc.) — helps us cross-reference known-good allowlist patterns for that host
- **Confirmation your host completed the allowlist** (paste any confirmation email they sent)
- **Royal MCP version** from WP Admin → Plugins
- **Whether the Activity Log** now shows entries during a failed attempt (any entry means the request reached WordPress — different diagnosis)

[Back to Troubleshooting Start Here](https://royalplugins.com/support/royal-mcp/troubleshooting-start-here/)
[Back to Royal MCP Support](https://royalplugins.com/support/royal-mcp/)
