Fix /.well-known/ Content-Type with a Cloudflare Transform Rule
If you’ve already placed the static OAuth metadata files in .well-known/ and they return HTTP/2 200 — but Claude Desktop still can’t connect — your host is serving them with the wrong Content-Type. On SiteGround (and other nginx hosts), .htaccess ForceType directives are silently ignored for these paths. A free Cloudflare Transform Rule rewrites the response Content-Type to application/json at the edge in about two minutes.
When You Need This Fix
You’re hitting the Content-Type issue if all of these are true:
The Telltale Signs
- You’ve already placed
oauth-authorization-serverandoauth-protected-resourceinpublic_html/.well-known/per the static-files article - Both URLs return
HTTP/2 200with the correct JSON body when youcurlthem curl -Ion either URL showscontent-type: text/plain(orapplication/octet-stream) — notapplication/json- You added a
.htaccessfile withForceType application/jsondirectives, but the Content-Type didn’t change - Claude Desktop / mcp-remote shows “Couldn’t reach the MCP server” despite OAuth metadata being reachable
.htaccess doesn’t work for these pathsSiteGround’s nginx layer serves files in /.well-known/ directly as static content and never falls through to Apache. Since .htaccess directives only apply when Apache handles the response, ForceType is silently ignored on these paths. The same is true on most managed hosts that intercept /.well-known/* at the nginx layer for ACME SSL renewals.
Why Cloudflare Is the Fix
RFC 8414 requires OAuth Authorization Server metadata responses to be served as application/json. RFC 9728 requires the same for Protected Resource Metadata. Strict MCP clients — Claude Desktop’s mcp-remote bridge in particular — reject metadata served with the wrong Content-Type, even if the JSON body is valid.
To fix this we need to override the response Content-Type. There are three places that could happen: in the application (PHP), in the web server (nginx), or at the edge (a CDN in front). On SiteGround, the first two are blocked:
- PHP can’t intercept these paths. nginx serves the static file directly and the request never reaches WordPress, so the plugin can’t set the Content-Type.
- nginx config is host-controlled. SiteGround migrated to Google Cloud infrastructure in 2026 and confirmed they no longer make per-customer nginx changes. There’s no path to add a MIME-type rule.
- An edge layer can rewrite headers freely. Cloudflare sits in front of the origin, sees the response on its way back to the client, and can override response headers per URL pattern with no host involvement.
Cloudflare’s free plan includes Transform Rules with up to 10 Modify Response Header rules — more than enough for this fix. You don’t need a paid plan, you don’t need APO, and you don’t need to ask SiteGround for anything.
The Fix — Cloudflare Modify Response Header Rule
Transform Rules only fire on requests that pass through Cloudflare’s edge. If your nameservers don’t point to Cloudflare yet, sign up for the free plan at dash.cloudflare.com/sign-up and follow Cloudflare’s nameserver migration prompt before continuing. The DNS A record for your domain must show the orange-cloud (proxied) icon — not gray-cloud (DNS-only). Setup typically takes 15–30 minutes including DNS propagation.
Open Transform Rules in the Cloudflare dashboard
Sign in at dash.cloudflare.com, select the zone for your domain, then in the left sidebar click Rules → Transform Rules.
Create a Modify Response Header rule
Click the Modify Response Header tab, then Create rule. Give it a memorable name like Force JSON Content-Type on OAuth metadata.
Set the matching expression
Switch to Edit expression mode and paste this filter exactly as written. It matches both OAuth metadata paths Royal MCP relies on:
(http.request.uri.path eq "/.well-known/oauth-authorization-server") or (http.request.uri.path eq "/.well-known/oauth-protected-resource")
Two URI paths, both joined with or. Cloudflare will validate the syntax inline; if it shows green, you’re good.
Configure the response header override
In the Then… section, set:
- Modify response header: Set static
- Header name:
Content-Type - Value:
application/json
Deploy the rule
Click Deploy. Cloudflare propagates Transform Rules globally within seconds — you don’t need to wait for DNS or cache purge.
Verify the override is live
From any terminal:
curl -sI https://YOUR-DOMAIN.com/.well-known/oauth-authorization-server | grep -i content-type curl -sI https://YOUR-DOMAIN.com/.well-known/oauth-protected-resource | grep -i content-type
Both should now show content-type: application/json. If they still show text/plain, double-check (a) the Transform Rule is enabled, (b) the URI path expressions match exactly, and (c) the DNS record is proxied (orange cloud) rather than DNS-only.
Reconnect Claude Desktop / Claude.ai
Quit and relaunch Claude Desktop, or click Reconnect in Claude.ai → Connectors. If the OAuth flow completes cleanly, you’re done. If you still see “Couldn’t reach the MCP server” and your Royal MCP Activity Log shows no auth activity, continue to the next section — Cloudflare’s default AI bots rule is most likely intercepting Anthropic’s backend before it ever reaches your site.
How to Confirm the Fix Worked
Three checks confirm the override is in place and Claude is happy:
Headers show application/json
The curl -sI commands above should show the corrected Content-Type. If they still show text/plain, the Transform Rule isn’t firing — recheck the proxied DNS record and the expression syntax.
Body content is unchanged
Run curl -s https://YOUR-DOMAIN.com/.well-known/oauth-authorization-server | head and confirm you still see your JSON. Transform Rules only modify headers; they never touch the body. If the body changed, something else is going on.
Claude Desktop logs show successful discovery
Inspect %APPDATA%\Claude\logs\mcp*.log on Windows, or ~/Library/Logs/Claude/mcp*.log on macOS, for entries from your reconnection attempt. A successful flow logs the OAuth metadata fetch and a Bearer token issuance. Any earlier “invalid Content-Type” or “couldn’t parse OAuth metadata” errors should be gone.
After the Transform Rule: Allow Anthropic Through Cloudflare’s AI Bots Rule
The Transform Rule above fixes the Content-Type, but putting Cloudflare in front of your site activates a second blocker by default. Cloudflare’s built-in Manage AI bots managed rule (under the “Cloudflare Bot Management rules for all plans” ruleset) blocks Anthropic’s backend IP ranges — including the IPs Claude uses for the server-to-server OAuth token exchange and post-auth tool calls.
The browser side of the OAuth flow originates from your own IP, so consent completes normally. But the moment Anthropic’s backend tries to call /token or any MCP tool endpoint, Cloudflare blocks the request at the edge before WordPress ever sees it. Symptom: Claude shows “Couldn’t reach the MCP server” and your Royal MCP Activity Log stays empty for the entire connection-attempt window.
Without Cloudflare in front, this rule never affects you. By proxying your domain through Cloudflare to apply the Transform Rule, you opt into Cloudflare’s default bot management — which actively blocks the IPs needed for OAuth to work. Background reading on the underlying mechanism.
The Fix — Skip Managed Rules for Royal MCP Paths
Path-based exclusion is more durable than IP-based: Anthropic’s IP allocations may grow over time, but the Royal MCP URL paths are stable and version-controlled in this plugin. Create a Custom WAF Rule that skips all managed rules — including AI bots and Super Bot Fight Mode — only for traffic destined for your MCP and OAuth endpoints. The rest of your site stays fully protected.
Open Custom WAF Rules
Cloudflare dashboard → Security → WAF → Custom rules tab → Create rule.
Name the rule and set the matching expression
Name it something memorable like Allow Royal MCP and OAuth paths through managed rules, then switch to Edit expression mode and paste this filter exactly as written:
(starts_with(http.request.uri.path, "/wp-json/royal-mcp/")) or (starts_with(http.request.uri.path, "/.well-known/oauth-")) or (http.request.uri.path eq "/authorize") or (http.request.uri.path eq "/token") or (http.request.uri.path eq "/register")
This matches every URL Royal MCP touches: the MCP REST namespace, the two OAuth metadata files, and the three root-rewritten OAuth endpoints (/authorize, /token, /register).
Set the action to Skip
In the Then take action… section:
- Action: Skip
- Skip features: check All managed rules AND Super Bot Fight Mode rules if you have it. The latter is the umbrella that contains the “Manage AI bots” rule specifically.
Leave the other Skip options unchecked — you only want to bypass managed rules for these paths, not your custom rules or rate limiting.
Deploy the rule
Click Deploy. Custom WAF rules propagate within seconds.
Reconnect and verify in Cloudflare Events
Try the Claude connection again. In the Cloudflare dashboard → Security → Events, filter to a 5-minute window covering your reconnect attempt and look for source IPs starting with 160.79. (Anthropic’s observed range). You should see entries for /register, /token, or /wp-json/royal-mcp/v1/mcp with action Allow and your new Custom Rule named as the matched rule. If you see Block from a different feature, expand the Skip features in your Custom Rule to cover it — the Events log names the exact rule + ruleset every time.
An IP-based Skip rule (e.g. ip.src in {160.79.104.0/22}) was the first workaround surfaced in this cascade, but it’s brittle: Anthropic’s IP allocations may expand without notice, and the day they add a new range the rule silently stops covering it. The Royal MCP URL paths, by contrast, are stable across plugin versions. Skip by path and the rule keeps working regardless of what Anthropic does with their network footprint.
Some accounts have additional security features that intercept at different phases — Browser Integrity Check, Cloudflare Access, or a separate Bot Fight Mode toggle on the dashboard root. The most reliable signal is Security → Events: every blocked request names the exact rule + ruleset that caught it, and you can extend your Skip rule to cover that specific feature. If everything in Events shows Allow but the connection still fails, the issue is past Cloudflare entirely — head to the Troubleshooting section below.
If the Rule Doesn’t Stick
The DNS record is gray-cloud (DNS-only)
Transform Rules only fire on proxied traffic. If the orange cloud icon next to your A record is gray, requests bypass Cloudflare entirely and hit SiteGround directly.
Fix
Cloudflare dashboard → DNS → Records. Click the gray-cloud icon next to your A record (and AAAA if present) to flip it to orange. Wait 30 seconds for the change to propagate, then re-run the curl test.
The expression has a typo and Cloudflare flagged it
Transform Rules require strict expression syntax. A missing parenthesis or quote will block the rule from saving.
Fix
Switch the expression editor to Edit expression mode and paste the filter from Step 3 exactly. Don’t hand-type it — quote characters and the URI paths must match exactly. Cloudflare will show a green checkmark when the syntax is valid.
SiteGround Cache or Cloudflare Cache is serving a stale 200 with old headers
Transform Rules fire on the response headers Cloudflare returns to the client. If a cached response from before the rule was deployed is still being served, you’ll see the old Content-Type.
Fix
Cloudflare dashboard → Caching → Configuration → Purge Everything. Then on SiteGround: SG Optimizer → Caching → Purge SG Cache. Re-run the curl test.
Claude Desktop still rejects after the headers are correct
If the Content-Type is now correct but Claude Desktop still shows “Couldn’t reach the MCP server”, the failure is somewhere else in the OAuth flow — not the metadata Content-Type.
Fix
Check the Claude Desktop logs (%APPDATA%\Claude\logs\mcp*.log) for the actual error. Common causes: relative URLs in your resource field, OAuth token endpoint cached at the edge, WAF blocking POST requests. The OAuth Fails on Managed Hosts article covers cache poisoning, and the static-files article covers the resource-field issue.
Alternative: Skip OAuth Entirely on Claude Desktop
If you only need Claude Desktop (not Claude.ai web) and you don’t want to set up Cloudflare, Royal MCP supports API-key authentication via mcp-remote’s --header flag. This bypasses OAuth completely — no /.well-known/, no Content-Type concerns, no Transform Rules.
Full walkthrough: Connect Claude Desktop via API Key (Skip OAuth).
Use this fallback if Cloudflare isn’t an option for your setup. For Claude.ai web (browser connector), the Transform Rule above is the only route — the API-key bypass works only for Claude Desktop.
Why We No Longer Recommend Asking SiteGround Support
Through April 2026, the long-term fix for this issue was to open a SiteGround support ticket asking them to add an nginx MIME type mapping for the two metadata paths. SiteGround had granted similar per-customer nginx exceptions in the past.
That changed when SiteGround migrated to Google Cloud infrastructure. As confirmed by SiteGround support to a Royal MCP customer in May 2026, they no longer make per-customer nginx configuration changes on the new platform — the previous escalation path is permanently closed. The Cloudflare Transform Rule documented above is the recommended replacement.
The Cloudflare Transform Rule approach works on any managed host where (a) /.well-known/* is served by nginx as static files, (b) .htaccess ForceType is silently ignored, and (c) you can’t edit nginx config directly. We’ve seen the same fingerprint on some o2switch, Hostinger, and Pressable configurations — the same Transform Rule fix applies.
Still Stuck?
If the Transform Rule is deployed, the headers show application/json, and Claude still can’t connect, contact support@royalplugins.com with:
Information We’ll Need
- Full
curl -ioutput (with body) for both/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource - Royal MCP version from WP Admin → Plugins
- Your domain
- Confirmation the DNS record is proxied (orange cloud) on Cloudflare
- Claude Desktop / Claude.ai error message after the failed connection
- Excerpt from
mcp*.logfrom the failed attempt (last 50 lines is plenty)
We respond within one business day.