OAuth Discovery Wrong Content-Type — ACME-First Host .well-known/ Intercept
Your Royal MCP OAuth discovery URLs return HTTP 200 with the correct JSON body, but the response Content-Type header is application/octet-stream (or text/plain, or missing entirely). You added a .htaccess inside /.well-known/ with ForceType, Header set Content-Type, or a RewriteRule — none of them changed anything. That’s the fingerprint of an ACME-first managed host serving the entire /.well-known/ directory through its own SSL/certificate handler, bypassing Apache/LiteSpeed and ignoring the directory’s .htaccess file entirely. Strict-JSON clients (Claude, ChatGPT, MCP DCR) reject the metadata even though the JSON body is valid. The fix is a root-level .htaccess rewrite that runs outside the intercepted directory.
The Telltale Fingerprint
All of these are true
- You created
public_html/.well-known/oauth-authorization-serverandoauth-protected-resourceas static files with the correct JSON bodies (per the setup docs) - Hitting either URL returns
HTTP/2 200with a valid JSON body when youcurlthem - But
curl -Ion the same URL showscontent-type: application/octet-stream(ortext/plain, or blank) — NOTapplication/json - You added a
.htaccessinside/.well-known/with<Files "oauth-authorization-server">ForceType application/json</Files>orHeader always set Content-Type "application/json", saved it, cleared caches — the response Content-Type didn’t change - You added
RewriteRuledirectives inside that same.htaccessto map the extensionless URLs to.jsoncopies — the rewrite never fired - Claude.ai or ChatGPT can’t complete OAuth discovery, or DCR fails at
/registerbecause clients following RFC 8414 refuse to parse metadata served with the wrongContent-Type
What this tells you: your .htaccess file inside /.well-known/ is being ignored entirely — not by nginx (as on SiteGround) but by the host’s ACME/SSL handler that has claimed the whole directory before Apache/LiteSpeed sees the request. ForceType only works when Apache/LiteSpeed handles the response. If the request never reaches your web server’s configured handler, no directive inside that .htaccess can take effect.
Confirm It’s This Pattern
Two probes settle whether you’re on this pattern versus one of the sibling patterns:
Probe 1: check the Content-Type header
curl -sI https://example.com/.well-known/oauth-authorization-server
(Substitute your bare domain.) You want to read the content-type: line in the response headers. If it’s application/json, you’re not on this pattern — check the sibling docs. If it’s application/octet-stream, text/plain, or missing, you’re in the right place.
Probe 2: confirm .htaccess inside /.well-known/ is being ignored
Add a deliberately-broken directive to public_html/.well-known/.htaccess:
# Deliberately broken syntax — should produce HTTP 500 if Apache/LiteSpeed reads this file FooBarNonsenseDirective anything
Reload any URL under /.well-known/. On a normal Apache/LiteSpeed host, invalid .htaccess syntax produces an HTTP 500 Internal Server Error. If instead your /.well-known/ URLs continue to serve normally (as if nothing changed), your web server never read that .htaccess file — the ACME handler intercepted the request before Apache/LiteSpeed got involved. Remove the broken line after testing.
Take a normal .htaccess file OUTSIDE /.well-known/ (like public_html/.htaccess, your WordPress root .htaccess), add the same broken directive, reload any page on your site. It will 500 — that’s Apache/LiteSpeed reading the file. If that 500 appears while /.well-known/ requests keep serving cleanly, you’ve confirmed the directory is completely detached from Apache/LiteSpeed’s handler path. Same test, different angle.
Known-Affected Hosts
This pattern appears on hosts that implemented the ACME challenge path (/.well-known/acme-challenge/, per RFC 8555) with a blanket directory-level intercept rather than a scoped subdirectory hook:
- Webempresa (Spain / Latin America) — WePanel + LiteSpeed + CloudLinux stack; confirmed via a customer support ticket
- Some SiteGround plans — specifically the plans where nginx has claimed the whole prefix; different variant than the standard SiteGround pattern (see the SiteGround-specific doc for that path)
- cPanel with AutoSSL enabled — AutoSSL’s DCV handler on some cPanel configurations claims the whole
/.well-known/prefix - WHM defaults on servers where the WHM administrator hasn’t narrowed AutoSSL to just
acme-challenge/ - Plesk with Let’s Encrypt in certain configurations
If your host isn’t on this list but the fingerprint above matches, the fix still applies — the pattern isn’t host-specific; it’s a class of SSL-handler misconfiguration that can show up on any managed WordPress host that runs ACME with a blanket intercept.
Fix Option A — Root .htaccess Rewrite (Fastest)
Since the .htaccess inside /.well-known/ is ignored, put the rewrite in your site root .htaccess — that file IS honored, because it’s outside the intercepted directory. The rewrite runs at the site root before the ACME handler evaluates the extensionless URL. Total time: about 5 minutes.
Create the .json copies of your metadata files
In public_html/.well-known/, keep your existing extensionless files (oauth-authorization-server, oauth-protected-resource) AND create identical copies with a .json extension:
cd /path/to/public_html/.well-known/ cp oauth-authorization-server oauth-authorization-server.json cp oauth-protected-resource oauth-protected-resource.json
Or via SFTP: duplicate each file and rename the copy with a .json suffix. Contents should be identical — same JSON body, same substitutions for your domain.
Add the rewrite to your site-root .htaccess
Open public_html/.htaccess (the WordPress root file, NOT the one inside /.well-known/). Add these two lines ABOVE the # BEGIN WordPress block:
RewriteEngine On RewriteRule ^\.well-known/oauth-authorization-server$ /.well-known/oauth-authorization-server.json [L] RewriteRule ^\.well-known/oauth-protected-resource$ /.well-known/oauth-protected-resource.json [L]
If RewriteEngine On is already present elsewhere in the file (which it will be for any standard WordPress .htaccess), you can omit that line. Save the file.
Verify the Content-Type is now correct
curl -sI https://example.com/.well-known/oauth-authorization-server
The response should now show content-type: application/json. If it does, discovery is fixed and Claude/ChatGPT will proceed with the OAuth flow normally.
Retry the client connection
Open Claude or ChatGPT and reconnect the Royal MCP custom connector. OAuth discovery should complete and the DCR / authorize / token flow should proceed normally.
The rewrite pattern matches on the URL path as WordPress’s root receives it, BEFORE the ACME handler evaluates the extensionless path. Because you’re rewriting to a .json-suffixed URL, the ACME handler doesn’t claim it (it only owns extensionless paths under /.well-known/), and the request falls through to Apache/LiteSpeed serving a .json static file with the correct Content-Type by default. No .htaccess inside /.well-known/ needed — you sidestep the intercepted directory entirely.
Fix Option B — Ask Your Host to Narrow the Intercept
This is the permanent-fix path if Option A isn’t practical or if you want the class of issue solved for anything else you install later (WebFinger, security.txt, etc. all use /.well-known/ too).
What to ask your host:
Sample support ticket text you can paste
Subject: /.well-known/ intercept scope — request to narrow to acme-challenge only
Hi,
Your server is currently serving my entire /.well-known/ directory
through your ACME/SSL handler (which is standard on Apache/LiteSpeed
managed hosts with AutoSSL / Let's Encrypt). This is causing an issue
with OAuth 2.0 discovery for a WordPress plugin I use: files at
/.well-known/oauth-authorization-server and
/.well-known/oauth-protected-resource are being served with
Content-Type: application/octet-stream because the .htaccess file
inside /.well-known/ is not being read.
Could you narrow the ACME handler to only intercept requests under
/.well-known/acme-challenge/ (which is what RFC 8555 actually
requires), and let the rest of /.well-known/ fall through to Apache
/ LiteSpeed and honor the local .htaccess? That's the standard
scoping and would fix this without any change on my end.
RFC references, in case they're useful:
- RFC 8555 §8.3 (ACME HTTP-01 uses .well-known/acme-challenge/*)
- RFC 8414 (OAuth 2.0 Authorization Server Metadata; requires
.well-known/oauth-authorization-server)
- RFC 9728 (OAuth 2.0 Protected Resource Metadata; requires
.well-known/oauth-protected-resource)
Thanks!Response time varies by host. This is a normal ask — WordPress security and integration plugins increasingly serve at /.well-known/* and hosts have been adapting.
Fix Option C — API-Key Auth (Escape Hatch for Claude Desktop / Claude Code)
If Option A doesn’t work on your specific setup and Option B is going to take too long, Royal MCP supports API-key authentication alongside OAuth. Skip OAuth and the .well-known/ dependency entirely by generating an API key in WP Admin → Royal MCP → Settings → General Settings and configuring your client with Authorization: Bearer <key>.
Claude.ai’s browser Add custom connector dialog exposes only four fields: Name, Remote MCP server URL, OAuth Client ID, OAuth Client Secret. There is no custom-header field, no API-key field. So the API-key path is a working fallback for Claude Desktop and Claude Code CLI only. If you specifically need claude.ai web against this host, you must resolve the discovery Content-Type via Option A or Option B.
Full walkthrough for the API-key path: Connect Claude Desktop via API Key →
Variant: Host Also Intercepts Root Paths Like /register, /authorize, /token
If curl https://example.com/register returns a 404 that clearly isn’t from WordPress (no x-httpd header, no WordPress branding, tiny response body), and Save Permalinks doesn’t restore Royal MCP’s expected 405 “POST method required” response, some hosts also intercept root-level paths before WordPress evaluates rewrites. Same class of problem, different layer.
The clean fix in that case is Royal MCP’s royal_mcp_oauth_rewrite_paths filter, which relocates the three OAuth endpoints to a namespace your host DOES route to WordPress (typically /wp-json/royal-mcp/v1/*, which every host respects because WP-JSON is standard REST). Add to your theme’s functions.php or a mu-plugin:
add_filter( 'royal_mcp_oauth_rewrite_paths', function () {
return [
'authorize' => 'wp-json/royal-mcp/v1/authorize',
'token' => 'wp-json/royal-mcp/v1/token',
'register' => 'wp-json/royal-mcp/v1/register',
];
} );Then re-save Permalinks. Royal MCP’s own /.well-known/oauth-authorization-server metadata reads from the same filter, so discovery auto-updates to advertise the relocated URLs — but if you’re serving discovery as static files (per Option A above), you’ll ALSO need to manually update the authorization_endpoint, token_endpoint, and registration_endpoint values in the static JSON to point at the relocated URLs and re-upload.
Why We Can’t Fully Relocate Off /.well-known/
You might wonder why Royal MCP doesn’t just serve OAuth metadata from a WordPress route entirely and skip /.well-known/ altogether — that would sidestep this whole problem class.
RFC 8414 §3.1 requires OAuth 2.0 Authorization Server Metadata to be served at /.well-known/oauth-authorization-server (or a suffix-appended variant). Clients following the spec probe that path directly — they don’t discover it through any other channel. Anything served elsewhere would not be found by any compliant client, so we can’t opt out. Same for RFC 9728 Protected Resource Metadata.
What the plugin CAN do (and does) is:
- Serve the metadata dynamically from PHP when WordPress rewrites can claim the path — happy path, no static files needed
- Detect when the well-known path isn’t reachable and surface an admin notice pointing at this doc + the sibling docs
- Advertise the correct endpoint URLs in the metadata body so relocation via
royal_mcp_oauth_rewrite_pathsstays discoverable - Let you customize the metadata URL via the
royal_mcp_protected_resource_metadata_urlfilter for the protected-resource endpoint (where the spec permits some flexibility)
The static-file workaround exists because the alternative — declaring the plugin incompatible with the entire class of managed WordPress hosts — is a worse outcome for customers than a two-minute config fix.
Still Stuck?
Community Support (free) — wp.org Plugin Forum
Post a new thread at wordpress.org/support/plugin/royal-mcp/. Include:
- Your hosting provider name
- The full
curl -sIoutput for/.well-known/oauth-authorization-serverbefore and after applying Option A - The contents of your site-root
.htaccessabove the# BEGIN WordPressblock (redact any sensitive values) - Royal MCP version
- Which client is failing (Claude.ai web / Claude Desktop / ChatGPT / Claude Code)
Premium Support (paid)
For direct one-on-one help with 24-hour SLA, see Premium Support ($149/year).