laravel/framework · warning · OriginMismatchException

Origin mismatch.

Error message

Origin mismatch.

What it means

Within hasValidOrigin(), if the Sec-Fetch-Site header is neither 'same-origin' nor (when allowed) 'same-site', and the static flag PreventRequestForgery::$originOnly is true, an OriginMismatchException is thrown. This is the strict, token-free mode that relies purely on the browser's Fetch Metadata to deny cross-origin requests. Unlike TokenMismatchException it is not auto-converted to 419; it surfaces as a 403.

Source

Thrown at src/Illuminate/Foundation/Http/Middleware/PreventRequestForgery.php:156

     * @param  \Illuminate\Http\Request  $request
     * @return bool
     *
     * @throws \Illuminate\Http\Exceptions\OriginMismatchException
     */
    protected function hasValidOrigin($request)
    {
        $secFetchSite = $request->header('Sec-Fetch-Site');

        if ($secFetchSite === 'same-origin') {
            return true;
        }

        if ($secFetchSite === 'same-site' && static::$allowSameSite) {
            return true;
        }

        if (static::$originOnly) {
            throw new OriginMismatchException('Origin mismatch.');
        }

        return false;
    }

    /**
     * Determine if the session and input CSRF tokens match.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function tokensMatch($request)
    {
        $token = $this->getTokenFromRequest($request);

        return is_string($request->session()->token()) &&
               is_string($token) &&
               hash_equals($request->session()->token(), $token);

View on GitHub (pinned to e0f6eb3518)

Solutions

  1. Move API/webhook routes out of the web middleware stack (use api guard or Sanctum) so PreventRequestForgery does not run on them.
  2. If same-site subdomain requests must pass, enable PreventRequestForgery::allowSameSite().
  3. Add the affected URIs to $except and protect them with a different mechanism (signature, bearer token).
  4. Turn off originOnly if you genuinely need cross-origin browser requests: PreventRequestForgery::originOnly(false).

Example fix

// before
PreventRequestForgery::originOnly();
// webhook POST from third party -> OriginMismatchException

// after — exclude the webhook, validate signature separately
PreventRequestForgery::except(['/webhooks/stripe']);
// and in the controller, abort_unless(hash_equals($known, $sig), 403);
Defensive patterns

Strategy: validation

Validate before calling

// On the caller side, only send state-changing requests from a browser same-origin context,
// or move API/webhook routes out of the PreventRequestForgery middleware.
PreventRequestForgery::except(['/api/*', '/webhooks/*']);

Type guard

// Server-side guard for non-browser callers: rely on bearer tokens instead of Fetch Metadata
if ($request->bearerToken() && $request->user('sanctum')) { /* bypass origin check */ }

Try / catch

use Illuminate\Http\Exceptions\OriginMismatchException;

try {
    $response = $next($request);
} catch (OriginMismatchException $e) {
    // surface 403; do NOT widen trust — fix routing or use bearer auth
}

Prevention

When it happens

Trigger: originOnly mode is enabled (e.g. PreventRequestForgery::originOnly()) AND a request arrives with Sec-Fetch-Site: cross-site or no Sec-Fetch-Site header at all. Common with non-browser clients (curl, Postman, server-to-server) that never send Fetch Metadata headers.

Common situations: Mixing originOnly strict mode with API/webhook consumers that are not browsers. Browsers that suppress Sec-Fetch-Site (older versions, some privacy extensions). Webhooks from third parties hitting originOnly-guarded routes.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/961879e6b253b95c. Report an issue: GitHub.