TryGhost/Ghost · error · BadRequestError

Request made from incorrect origin. Expected '${session.orig

Error message

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

What it means

A BadRequestError from the second CSRF check in `cookieCsrfProtection`. After the request origin passes the admin-URL check, the request origin must also match the origin stored on the session when it was created (`session.origin`). A mismatch indicates the request originates from a different host than the one that authenticated the session — a possible session-reuse or fixation attempt.

Source

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

        // 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}'.`
            });
        }
    }

    /**
     * isVerificationRequired
     * Determines if 2FA verification is required based on site settings
     * @returns {boolean}
     */
    function isVerificationRequired() {
        return getSettingsCache('require_email_mfa') === true;
    }

    async function assignUserToSession({
        session,
        user,
        origin,

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Have affected users log out and back in so the session stores the current admin origin.
  2. Fix the `url`/`admin.url` config so the request origin is stable and matches what is stored on new sessions.
  3. Ensure all nodes behind the load balancer derive the same `Origin` (consistent proxy headers).
  4. Avoid reusing session cookies across origins/tools; use the Admin API key flow for integrations instead.

Example fix

// before: changed admin url mid-session, requests now carry new origin
// session.origin still holds 'https://old.example.com'

// after: invalidate stale sessions after a domain/config change
await db.query('TRUNCATE TABLE sessions'); // or ask users to re-auth
// and pin config.url to the stable public origin
Defensive patterns

Strategy: try-catch

Validate before calling

// Server-side config validation: ensure request origin equals session.origin before acting
function assertSessionOrigin(req) {
  const origin = getOrigin(req);
  if (req.session?.origin && req.session.origin !== origin) {
    throw new Error('Session originated on a different host; re-authentication required');
  }
}

Type guard

const sessionMatchesOrigin = (session, origin) => !session?.origin || session.origin === origin;

Try / catch

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

Prevention

When it happens

Trigger: A request that passes the admin-URL origin check but whose `Origin`/`Referer`-derived origin differs from `session.origin`. This occurs when a session cookie is replayed from a different origin, when the admin URL changes after login (so new requests have a new origin but the session keeps the old one), or when the same cookie is used across two domains.

Common situations: The admin `url` config was changed after users logged in, invalidating their stored session origins; a load-balanced setup where `Origin` resolves differently per node; a user copied cookies to another tool/domain; mixed `http` vs `https` access changing the derived origin.

Related errors


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