{"record":{"id":"714ed15a225aafc8","repo":"TryGhost/Ghost","slug":"request-made-from-incorrect-origin-expected-se","errorCode":null,"errorMessage":"Request made from incorrect origin. Expected '${session.origin}' received '${origin}'.","messagePattern":"Request made from incorrect origin\\. Expected '(.+?)' received '(.+?)'\\.","errorType":"http","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"ghost/core/core/server/services/auth/session/session-service.js","lineNumber":149,"sourceCode":"        // Check that the origin matches the admin URL to prevent cross-origin\n        // requests (e.g. no-cors form submissions from phishing sites)\n        const adminUrl = urlUtils.getAdminUrl() || urlUtils.getSiteUrl();\n        const adminOrigin = new URL(adminUrl).origin;\n\n        if (origin !== adminOrigin) {\n            throw new BadRequestError({\n                message: `Request made from incorrect origin. Expected '${adminOrigin}' received '${origin}'.`\n            });\n        }\n\n        // If there is no origin on the session object it means this is a *new*\n        // session, that hasn't been initialised yet. So we don't need CSRF protection\n        if (!session.origin) {\n            return;\n        }\n\n        if (session.origin !== origin) {\n            throw new BadRequestError({\n                message: `Request made from incorrect origin. Expected '${session.origin}' received '${origin}'.`\n            });\n        }\n    }\n\n    /**\n     * isVerificationRequired\n     * Determines if 2FA verification is required based on site settings\n     * @returns {boolean}\n     */\n    function isVerificationRequired() {\n        return getSettingsCache('require_email_mfa') === true;\n    }\n\n    async function assignUserToSession({\n        session,\n        user,\n        origin,","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/ghost/core/core/server/services/auth/session/session-service.js#L131-L167","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Have affected users log out and back in so the session stores the current admin origin.","Fix the `url`/`admin.url` config so the request origin is stable and matches what is stored on new sessions.","Ensure all nodes behind the load balancer derive the same `Origin` (consistent proxy headers).","Avoid reusing session cookies across origins/tools; use the Admin API key flow for integrations instead."],"exampleFix":"// before: changed admin url mid-session, requests now carry new origin\n// session.origin still holds 'https://old.example.com'\n\n// after: invalidate stale sessions after a domain/config change\nawait db.query('TRUNCATE TABLE sessions'); // or ask users to re-auth\n// and pin config.url to the stable public origin","handlingStrategy":"try-catch","validationCode":"// Server-side config validation: ensure request origin equals session.origin before acting\nfunction assertSessionOrigin(req) {\n  const origin = getOrigin(req);\n  if (req.session?.origin && req.session.origin !== origin) {\n    throw new Error('Session originated on a different host; re-authentication required');\n  }\n}","typeGuard":"const sessionMatchesOrigin = (session, origin) => !session?.origin || session.origin === origin;","tryCatchPattern":"try {\n  await api.admin.someAction();\n} catch (err) {\n  if (err.type === 'BadRequestError' && /incorrect origin/i.test(err.message) && /received/.test(err.message)) relogin();\n  else throw err;\n}","preventionTips":["After changing admin url config, clear/rotate sessions so users re-auth.","Never reuse session cookies across origins or in CLI tools — use API keys.","Ensure all load-balanced nodes derive the same request origin."],"tags":["csrf","origin","security","session","config"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}