TryGhost/Ghost · error · BadRequestError

Request made from incorrect origin. Expected '${adminOrigin}

Error message

Request made from incorrect origin. Expected '${adminOrigin}' received '${origin}'.

What it means

A BadRequestError from CSRF/origin protection in `cookieCsrfProtection`. Every authenticated request's `Origin` (or `Referer`-derived origin) must match the configured admin URL origin (`urlUtils.getAdminUrl()` falling back to site URL). A mismatch means the request came from a different host, which the server treats as a cross-origin form post and rejects before touching the session.

Source

Thrown at ghost/core/core/server/services/auth/session/session-service.js:137

    }

    /**
     * cookieCsrfProtection
     *
     * @param {Req} req
     * @param {Session} session
     * @returns {Promise<void>}
     */
    function cookieCsrfProtection(req, session) {
        const origin = getOriginOfRequest(req);

        // Check that the origin matches the admin URL to prevent cross-origin
        // requests (e.g. no-cors form submissions from phishing sites)
        const adminUrl = urlUtils.getAdminUrl() || urlUtils.getSiteUrl();
        const adminOrigin = new URL(adminUrl).origin;

        if (origin !== adminOrigin) {
            throw new BadRequestError({
                message: `Request made from incorrect origin. Expected '${adminOrigin}' received '${origin}'.`
            });
        }

        // If there is no origin on the session object it means this is a *new*
        // session, that hasn't been initialised yet. So we don't need CSRF protection
        if (!session.origin) {
            return;
        }

        if (session.origin !== origin) {
            throw new BadRequestError({
                message: `Request made from incorrect origin. Expected '${session.origin}' received '${origin}'.`
            });
        }
    }

    /**

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Set `url` (and `admin.url` if used) in `config.*.json` to the exact origin users reach the admin through, including scheme.
  2. Ensure the reverse proxy preserves/sets `X-Forwarded-Host`/`X-Forwarded-Proto` and that Ghost trusts the proxy (`url` matches the public origin).
  3. Make all admin links/embeds use the configured admin origin consistently.
  4. After a domain migration, update `url` and run `ghost setup ssl`/update the config to the new origin.

Example fix

// before — config.production.json
{
  "url": "http://example.com",
  "admin": {"url": "https://admin.example.com"}
}

// after — single consistent public origin
{
  "url": "https://example.com",
  "admin": {"url": "https://example.com/ghost"}
}
Defensive patterns

Strategy: validation

Validate before calling

function assertOriginMatchesConfig(reqOrigin, adminUrl) {
  const expected = new URL(adminUrl).origin;
  if (reqOrigin !== expected) {
    throw new Error(`Origin mismatch: expected ${expected}, got ${reqOrigin}. Fix config.url/admin.url.`);
  }
}

Type guard

const originsMatch = (reqOrigin, adminUrl) => {
  try { return reqOrigin === new URL(adminUrl).origin; } catch { return false; }
};

Try / catch

try {
  await api.admin.someAction();
} catch (err) {
  if (err.type === 'BadRequestError' && /incorrect origin/i.test(err.message)) fixConfigOrigin();
  else throw err;
}

Prevention

When it happens

Trigger: An authenticated request whose `Origin` header (or `Referer`-derived origin) differs from the admin URL origin. Happens with a misconfigured `url`/`admin.url` in config, behind a proxy that rewrites the host, when accessing admin via an alternate domain, or when a phishing/no-cors form posts to the admin API.

Common situations: Config `url` set to `http://example.com` but the user accesses `https://example.com`; a reverse proxy/Cloudflare rewrites Host and the `Origin` becomes the proxy host; the site was migrated to a new domain but config wasn't updated; the admin is reached via an IP or localhost while `url` is the public domain.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/b01cb7b4e5be61f3. Report an issue: GitHub.