Support / Royal MCP / ACME-First Host .well-known/ Intercept

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.

Different from the SiteGround / nginx pattern

SiteGround’s nginx intercepts /.well-known/ too, but returns a static 404 for anything you don’t explicitly put there — and the fix on SiteGround is either the static-file drop or the Cloudflare Transform Rule. This page is for the different pattern where your files ARE reachable and DO serve the right body, but the Content-Type comes out wrong because the host’s ACME handler owns the directory. Look at the diagnostic below to confirm which pattern applies to you.

The Telltale Fingerprint

All of these are true

  • You created public_html/.well-known/oauth-authorization-server and oauth-protected-resource as static files with the correct JSON bodies (per the setup docs)
  • Hitting either URL returns HTTP/2 200 with a valid JSON body when you curl them
  • But curl -I on the same URL shows content-type: application/octet-stream (or text/plain, or blank) — NOT application/json
  • You added a .htaccess inside /.well-known/ with <Files "oauth-authorization-server">ForceType application/json</Files> or Header always set Content-Type "application/json", saved it, cleared caches — the response Content-Type didn’t change
  • You added RewriteRule directives inside that same .htaccess to map the extensionless URLs to .json copies — the rewrite never fired
  • Claude.ai or ChatGPT can’t complete OAuth discovery, or DCR fails at /register because clients following RFC 8414 refuse to parse metadata served with the wrong Content-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.

A one-shot test that’s equally decisive

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:

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.

Why this works

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>.

🛑 This escape hatch does NOT work with claude.ai web

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:

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:

Premium Support (paid)

For direct one-on-one help with 24-hour SLA, see Premium Support ($149/year).