# SiteGround Returns 404 for /.well-known/ — Royal MCP OAuth Fix

> SiteGround returns an nginx 404 for /.well-known/oauth-authorization-server before WordPress sees it, blocking Royal MCP OAuth. Two static JSON files fix it.

- Canonical: <https://royalplugins.com/support/royal-mcp/siteground-well-known-404/>
- HTML version: <https://royalplugins.com/support/royal-mcp/siteground-well-known-404/>

---

If you’ve already added SG Optimizer cache exclusions and Claude.ai still returns “Authorization with the MCP server failed” with an empty Royal MCP Activity Log, you’re hitting a separate, deeper issue: SiteGround’s nginx layer reserves `/.well-known/` for its own use (ACME SSL renewals) and serves a static 404 for any other path under it — before WordPress ever sees the request. Drop two static JSON files in your webroot and the OAuth handshake completes immediately.

Before applying these advanced steps

Royal MCP connection issues that look host-specific are usually a plugin conflict, cache layer, or stale OAuth state — about 90% resolve in our [**4-step basic checklist**](https://royalplugins.com/support/royal-mcp/troubleshooting-start-here/). If you haven’t run through that yet, do it first — this article assumes the basics are already ruled out.

### Try the cache-exclusion fix first

Most SiteGround OAuth failures are resolved by adding the SG Optimizer Dynamic Cache exclusions described in [OAuth Fails on Managed Hosts → Option A](https://royalplugins.com/support/royal-mcp/oauth-fails-on-managed-host/#fix). If you’ve done that and the Activity Log is *still* empty, this page is for you. The two issues are separate root causes and you may need both fixes.

📍 New here? Start with the complete SiteGround playbook

This page covers **one layer** of the SiteGround setup. If you’re just getting started, work through the [**Royal MCP on SiteGround playbook**](https://royalplugins.com/support/royal-mcp/royal-mcp-on-siteground/) first, which sequences all three layers in the correct order.

Want the simplest fix? Skip OAuth on Claude Desktop entirely.

If you only need to connect Claude Desktop (not Claude.ai web), you can bypass OAuth completely with a two-line change to your Claude Desktop config — no `/.well-known/` files, no static-file workaround, no SiteGround tickets. Confirmed working on SiteGround GrowBig.

[**See: Connect Claude Desktop via API Key (Skip OAuth) →**](https://royalplugins.com/support/royal-mcp/connect-claude-desktop-api-key/)

## Symptoms

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

#### The Telltale Signs

- You’ve already added SG Optimizer Dynamic Cache exclusions for the OAuth paths and purged the cache
- A `curl -I` against your Royal MCP REST endpoint shows `x-cache-enabled: False` — cache is confirmed off for that path
- Hitting `https://example.com/.well-known/oauth-authorization-server` returns a **tiny 404 from nginx** (no `X-Httpd` header, no CORS headers, no WordPress branding) — the request died at the web server, not at WordPress
- Royal MCP is on version 1.4.13 or newer (verifiable in WP Admin → Plugins)

Don’t use the Activity Log as a diagnostic for this issue

An empty WP Admin → Royal MCP → Activity Log is **expected behavior** for OAuth-connected MCP sessions and is not a sign of failure. The Activity Log captures requests to the legacy REST endpoints (`/posts`, `/pages`, `/media`, etc.) but does not currently log requests to the modern MCP JSON-RPC endpoint at `/wp-json/royal-mcp/v1/mcp`, which is what Claude.ai actually uses. We’re closing this gap in a future release. For diagnosing this `/.well-known/` 404 issue, use the `curl` tests in the next section instead — they’re definitive.

## How to Confirm It’s the nginx `/.well-known/` Intercept

Three quick curl tests will leave no doubt about the diagnosis. Run them from any terminal — replace `example.com` with your actual domain.

#### Test the OAuth discovery URL Claude.ai uses

This is the URL the MCP client probes during connection setup:

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

If you see `HTTP/1.1 404 Not Found` with `Content-Length: 93` (or some other tiny number) and **no** `X-Httpd` header, that’s nginx serving its default 404 page directly. The request never reached WordPress.

#### Confirm with any other `/.well-known/` path

This proves the intercept is on the entire `/.well-known/` namespace, not specific to OAuth:

```
curl -I https://example.com/.well-known/security.txt
curl -I https://example.com/.well-known/anything-random
```

Identical 404 fingerprint confirms SiteGround’s nginx is shadowing the entire path prefix.

#### Compare against a normal WordPress 404 to see the difference

```
curl -s https://example.com/this-does-not-exist/ | head -20
```

This returns a full WordPress 404 page (HTML, your site’s language, theme styling), confirming WordPress *does* handle other unknown paths correctly. The `/.well-known/` behavior is unique — that’s the smoking gun.

Why SiteGround support may say “the files don’t exist”

If you opened a SiteGround support ticket and they replied that the `/.well-known/` files don’t exist on your server, that’s actually the correct behavior — and the perfect setup for this fix. SiteGround’s nginx **will** serve files from `/.well-known/` if they physically exist in your webroot. It just won’t fall through to WordPress when they don’t. So we’re going to put two real files there.

## The Fix — Two Static Files in Your Webroot

You’ll create two text files in `public_html/.well-known/` with no file extension. nginx will serve them directly, Claude.ai will read the OAuth metadata it needs, and the authorization handshake will complete normally.

Substitute your BARE domain — NOT your full URL

Both files contain absolute URLs that must match your site. In each JSON template below, do a Find & Replace of **`example.com`** → **your bare domain** (e.g. `yoursite.com` or `www.yoursite.com` if your site uses www). **Do NOT include `https://` in your replacement.** The `https://` is already in the template; if you replace `example.com` with `https://yoursite.com` you’ll end up with `https://https://yoursite.com/authorize` in the saved file, and OAuth will fail with `DNS_PROBE_FINISHED_NXDOMAIN` / "site can’t be reached" in the browser. (This trips customers up regularly. Read the resulting file once after saving to confirm there’s only ONE `https://` in front of your domain.)

#### Open SiteGround File Manager (or SFTP)

In Site Tools → Site → **File Manager**, navigate to your WordPress webroot — the folder containing `wp-config.php` and `wp-content/`. (For the primary domain on a SiteGround account, this is typically `public_html/`.)

#### Create the `.well-known` folder if it doesn’t exist

The folder name starts with a dot. Some file managers hide dot-prefixed entries by default — if you don’t see it, enable “Show hidden files” or just try to create it; the file manager will tell you if it already exists.

#### Create the first file: `oauth-authorization-server`

Inside `.well-known/`, create a new file named exactly `oauth-authorization-server` (no extension, no `.txt` or `.json`). Paste this JSON, then in your text editor do a Find & Replace: change every instance of `example.com` to your bare domain. **Use your bare domain only — do NOT include `https://` in the replacement.** The `https://` is already in the template; if you replace `example.com` with `https://yoursite.com` you’ll end up with `https://https://yoursite.com/authorize` (doubled prefix), and OAuth will fail with a "site can’t be reached" / `DNS_PROBE_FINISHED_NXDOMAIN` error in the browser.

```
{
  "issuer": "https://example.com",
  "authorization_endpoint": "https://example.com/authorize",
  "token_endpoint": "https://example.com/token",
  "registration_endpoint": "https://example.com/register",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none", "client_secret_post"],
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["mcp:full"],
  "service_documentation": "https://royalplugins.com/support/royal-mcp/"
}
```

Correct example: if your site is `https://yoursite.com`, the first line should end up as `"issuer": "https://yoursite.com"` — NOT `"issuer": "https://https://yoursite.com"`. Save the file when done.

#### Create the second file: `oauth-protected-resource`

Same folder, named exactly `oauth-protected-resource` (no extension). Paste this JSON, then Find & Replace `example.com` with your **bare domain only** (no `https://` — same warning as the previous file):

```
{
  "resource": "https://example.com/wp-json/royal-mcp/v1",
  "authorization_servers": ["https://example.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["mcp:full"]
}
```

Save the file.

The `resource` field MUST be an absolute URL

After substitution the value should look like `"resource": "https://yoursite.com/wp-json/royal-mcp/v1"` — do not leave it as a relative path like `/wp-json/royal-mcp/v1` or `wp-json/royal-mcp/v1`. RFC 9728 requires this field to be a fully-qualified absolute URL, and Claude.ai/Claude Desktop will resolve a relative path against the metadata URL itself, producing a malformed nginx request like `.well-known/oauth-protected-resource/wp-json/royal-mcp/v1/mcp` that returns 404. The connection will fail silently. Same rule applies to `authorization_servers` — absolute URLs only.

#### Verify both files are now reachable AND have the correct content

From any terminal (substitute `example.com` for your bare domain):

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

You should see your JSON body. **Read the response carefully** — if you see `https://https://` anywhere (doubled prefix), you replaced `example.com` with your full URL (including `https://`) instead of just the bare domain. Edit the file and remove the extra `https://`. If you still see 404, double-check the folder name starts with a dot and the file names have no extension.

#### Retry the Claude.ai connection

Open Claude.ai → Connectors → Royal MCP and click **Connect** (or **Reconnect**) again. The authorize flow should complete and Royal MCP’s Activity Log will now show entries the moment you click “Authorize.”

If Claude Desktop still rejects the response (Content-Type issue)

Strict MCP clients — Claude Desktop’s `mcp-remote` bridge in particular — require these metadata responses to be served as `application/json`. SiteGround’s nginx serves the static files as `text/plain`, and `.htaccess ForceType` directives are **silently ignored on these paths** because nginx never falls through to Apache for `/.well-known/*`. SiteGround also confirmed in May 2026 that they no longer make per-customer nginx config changes since their move to Google Cloud, so the host-side fix is closed.

The reliable answer is a free Cloudflare Transform Rule that overrides the response Content-Type at the edge. Two-minute setup, walkthrough at [**Fix /.well-known/ Content-Type with a Cloudflare Transform Rule →**](https://royalplugins.com/support/royal-mcp/siteground-cloudflare-content-type-fix/)

## Why Does SiteGround Block `/.well-known/`?

The `/.well-known/` URL prefix is reserved by RFC 5785 for “well-known” URIs that need a predictable, host-level location — things like ACME SSL certificate challenges (`/.well-known/acme-challenge/`), `security.txt`, and OAuth 2.0 / OpenID Connect discovery metadata (RFC 8414).

SiteGround’s nginx layer is configured to serve `/.well-known/acme-challenge/*` directly so their automated Let’s Encrypt renewals can complete without going through WordPress (which might redirect, return a wrong content type, or be slow). To keep that mechanism reliable, nginx claims the entire `/.well-known/` path prefix at the host level: it serves files that physically exist there, and returns a 404 for anything that doesn’t — without falling through to PHP.

That’s great for ACME, but it means RFC 8414 OAuth discovery URLs (which Claude.ai and every other MCP client are required to probe) never reach WordPress. The Royal MCP rewrite rule that maps `/.well-known/oauth-authorization-server` to a WordPress query var works on every other host we’ve tested — just not on SiteGround, because nginx never lets the request get that far.

Other hosts where this might apply

SiteGround is the most common case but the same pattern can appear on any host that uses nginx to claim `/.well-known/*` for ACME or other system-level uses — some o2switch, Hostinger, and Pressable configurations have shown the same fingerprint. If your `curl` shows the same tiny nginx 404 for `/.well-known/anything`, this fix applies to you too.

## What Royal MCP 1.4.14 Already Does for You

Royal MCP 1.4.14 (shipped May 2026) detects this exact failure mode automatically. When the plugin loads on a site where `/.well-known/oauth-authorization-server` isn’t reachable, it surfaces a dismissible admin notice on the Plugins screen and Royal MCP settings linking back to this page. So future SiteGround customers will land here from inside their own WordPress admin instead of having to search for “Royal MCP couldn’t reach OAuth” first.

The plugin can’t auto-create the static files for you, though — SiteGround’s nginx layer that blocks `/.well-known/` reads from the same WordPress installation, so files written via PHP would be served the same way. The static-file step still has to be done manually via File Manager / SFTP.

## Faster Alternative for Claude Desktop: Skip OAuth Entirely

If your only goal is to get Claude Desktop connected, the static-file workaround above is more work than you need. You can bypass OAuth completely by passing a Royal MCP API key as a header through `mcp-remote`, the bridge Claude Desktop uses to talk to remote MCP servers. No `/.well-known/`, no consent screen, no SiteGround support ticket.

Quick version — full step-by-step at [**Connect Claude Desktop via API Key**](https://royalplugins.com/support/royal-mcp/connect-claude-desktop-api-key/):

1. Copy your API key from **WP Admin → Royal MCP → Settings**
2. Add this entry to your `claude_desktop_config.json`. Substitute `example.com` with your **bare domain** (e.g. `yoursite.com` — **do NOT include `https://`**; the protocol is already in the template) and `your-api-key-here` with the key from step 1:

   ```
   {
     "mcpServers": {
       "my-site": {
         "command": "npx",
         "args": [
           "-y",
           "mcp-remote",
           "https://example.com/wp-json/royal-mcp/v1/mcp",
           "--header",
           "Authorization:Bearer your-api-key-here"
         ]
       }
     }
   }
   ```

   After substitution, the URL line should look like `"https://yoursite.com/wp-json/royal-mcp/v1/mcp"` — NOT `"https://https://yoursite.com/..."`. If you see a doubled `https://`, you included the protocol in your replacement; remove the duplicate.
3. Quit and relaunch Claude Desktop.

This works regardless of whether the `/.well-known/` files are in place. It also works regardless of Content-Type, WAF rules, or any other front-end web server quirk — the only thing it needs is the `/wp-json/royal-mcp/v1/mcp` endpoint reachable, which your `curl` test in the verification step above already confirmed.

Why this works on SiteGround when OAuth doesn’t

OAuth discovery (RFC 8414) requires reaching `/.well-known/*` — that’s the path SiteGround intercepts. The API key path uses only `/wp-json/royal-mcp/v1/mcp`, which SiteGround routes through to WordPress normally. The static-file workaround is the only way to make Claude.ai web (browser-based) work; for Claude Desktop, the API-key bypass sidesteps the entire problem.

## Still Stuck? Two Support Paths

If you’ve worked through the steps above and your connection still fails:

### 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/). The Royal Plugins team monitors the forum regularly and community members often help disambiguate issues faster than email could. 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. Includes a 30-day “if it breaks again” follow-up window on every resolved ticket.

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

- **Full curl output** for both `/.well-known/oauth-authorization-server` and `/.well-known/oauth-protected-resource` (use `curl -i`, not just `-I`, so we see the body too)
- **Royal MCP version** from WP Admin → Plugins
- **Your domain** (so we can compare your JSON against what we’d expect)
- **The exact error message** Claude.ai shows after clicking Authorize
- Whether the **Activity Log** now shows *any* entries during the failed attempt (even one entry tells us the request reached WordPress)

Related well-known issues

If the 404 isn’t the right diagnosis, two adjacent failure modes auto-detect in Royal MCP 1.4.22+:

- [**OAuth Discovery Returns HTML Instead of JSON**](https://royalplugins.com/support/royal-mcp/well-known-served-as-html/) — a membership plugin (ARMember, MemberPress, Restrict Content Pro) intercepts the request and serves a login page at 200.
- [**Web Server 301-Redirects /register**](https://royalplugins.com/support/royal-mcp/oauth-register-trailing-slash-301/) — Nginx / Apache canonicalization adds a trailing slash; OAuth clients don’t follow 301 on POST.

[OAuth Fails on Managed Hosts (cache fix)](https://royalplugins.com/support/royal-mcp/oauth-fails-on-managed-host/)
[Back to Royal MCP Support](https://royalplugins.com/support/royal-mcp/)
