# OAuth Discovery Wrong Content-Type — ACME-First Host .well-known/ Intercept

> Royal MCP OAuth discovery returns 200 as application/octet-stream and .htaccess in /.well-known/ is ignored: an ACME-first host. Fix via root .htaccess.

- Canonical: <https://royalplugins.com/support/royal-mcp/acme-first-host-well-known-intercepted/>
- HTML version: <https://royalplugins.com/support/royal-mcp/acme-first-host-well-known-intercepted/>

---

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](https://royalplugins.com/support/royal-mcp/siteground-well-known-404/) or the [Cloudflare Transform Rule](https://royalplugins.com/support/royal-mcp/siteground-cloudflare-content-type-fix/). 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](https://datatracker.ietf.org/doc/html/rfc8555)) 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](https://royalplugins.com/support/royal-mcp/siteground-well-known-404/) 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.

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 →**](https://royalplugins.com/support/royal-mcp/connect-claude-desktop-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](https://datatracker.ietf.org/doc/html/rfc9728) 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_paths` stays discoverable
- Let you customize the metadata URL via the `royal_mcp_protected_resource_metadata_url` filter 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/](https://wordpress.org/support/plugin/royal-mcp/). Include:

- Your hosting provider name
- The full `curl -sI` output for `/.well-known/oauth-authorization-server` before and after applying Option A
- The contents of your site-root `.htaccess` above the `# BEGIN WordPress` block (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](https://royalplugins.com/premium-support/) ($149/year).

## Related discovery-layer issues

[SiteGround returns 404 for `/.well-known/`

Different pattern — SiteGround’s nginx returns a static 404 for non-existent paths under `/.well-known/` rather than an ACME handler owning the whole directory. Fix is static-file drop instead of root rewrite.](https://royalplugins.com/support/royal-mcp/siteground-well-known-404/)
[Cloudflare Transform Rule for Content-Type

Edge-layer alternative that works on any host if you can put Cloudflare in front. Overrides Content-Type at the CDN before it reaches the client.](https://royalplugins.com/support/royal-mcp/siteground-cloudflare-content-type-fix/)
[Discovery returns HTML instead of JSON

Different fingerprint — a membership plugin, theme template, or WAF returns an HTML login/access-denied page instead of JSON metadata.](https://royalplugins.com/support/royal-mcp/well-known-served-as-html/)
[Full curl diagnostic walkthrough

The complete 4-probe sequence — useful if you’re not sure which discovery issue applies.](https://royalplugins.com/support/royal-mcp/diagnose-mcp-with-curl/)

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