APP_URL used to decide what your emails link to. Now it decides which browsers can talk to your MCP server.
I ship MCP servers from Laravel, and I also call remote ones from Laravel. 1.2.0, published 11 November 2026, changes the contract on both sides.
This sits with the earlier Laravel MCP posts in Laravel at Scale: auth and audit logging, idempotency, and tool errors agents can recover from. Those posts are about what a tool is allowed to do. This one is about which browsers are allowed to call the server at all.
What changed in 1.2.0
The 1.2.0 release notes list two changes that matter here: “Validate the Origin header on MCP web routes” (PR #394) and “Stop following redirects in the MCP HTTP client transport” (PR #392).
PR #394 adds ValidateOrigin middleware to the POST route that Mcp::web() registers, and to the GET and DELETE routes that answer 405.
A request with no Origin header passes. The PR says SDKs, desktop clients and server-side connectors do not change.
A request whose Origin is not allowed gets HTTP 403, with a JSON-RPC body: code -32600, and the message Forbidden: The request origin is not allowed.
Then
Mcp::web() route from any origin.Now
Origin is present and is not allowed, the route returns 403. No Origin still passes.Why this is the spec, not a Laravel opinion
The MCP transports spec (2025-11-25) says servers MUST validate the Origin header on all incoming connections to prevent DNS rebinding, and MUST respond 403 when the header is present and invalid. It also says servers SHOULD implement proper authentication for all connections.
DNS rebinding is the hole this closes. A page you never wrote can use the visitor’s browser to call an MCP server on localhost or a private network. The PR spells that out, and the dns-rebinding-protection conformance scenario was removed from the package’s known-failures baseline in the same PR.
Origin checking is not authentication. It stops one browser-borne attack. Auth is still your job, and the auth and audit post is still the place to start.
The exact allow rule, and where it bites
The middleware in 1.2.0 allows: no Origin; any localhost, 127.0.0.1 or [::1] origin on any port; the origin of config('app.url'); and entries in config('mcp.allowed_origins'), which can be a string or an array. A * entry allows all origins.
Matching is scheme, host and port. Default ports are normalized away, and case is ignored. The package tests reject a subdomain of the app (https://evil.app.test), a different scheme (http://app.test when APP_URL is https://app.test), a different port, the opaque origin null, and file://.
The check compares against APP_URL, not the request Host. The test named “rejects a rebinding request whose host matches its origin” covers this.
So a wrong or stale APP_URL (http instead of https, an old domain, a staging host) will 403 any browser client on the real domain. A browser client on a sibling subdomain needs an explicit entry.
mcp.allowed_origins is read by the middleware, but the published config/mcp.php in v1.2.0 does not contain the key, and the Laravel 13.x MCP docs do not mention it. You add it yourself.
Do not set it to * just to make a client work. That turns the check off.
The client side changed too: redirects are now errors
PR #392 (merged 5 November 2026) sets allow_redirects to false on the HTTP client transport. A redirect from the MCP server now throws a TransportException with the message that the server “responded with a redirect … which is not followed”, instead of being followed.
If your Laravel app calls a remote MCP URL that redirects, for example an http to https hop, that call now fails. Store the final URL.
The same release, PR #395, returns JSON-RPC errors instead of 500s for malformed requests and params, so junk traffic stops showing up as server errors.
Who gets it, and what I would check this week
v1.2.0 requires illuminate/* at ^12.41.1 or ^13.0. v1.1.2 also allowed ^11.45.3. A Laravel 11 app will not resolve 1.2.0, so it does not get the Origin check from a composer update.
If you are on Laravel 12.41.1 or 13 and you run composer update, you get this default.
Key takeaways
- After 1.2.0, a browser request to
Mcp::web()whoseOriginis not yourAPP_URLor localhost gets 403. That is the MCP spec. APP_URLis now part of your MCP security. Fix it first. Addmcp.allowed_originsyourself, and never as*.- The HTTP client now refuses redirects. Laravel 11 apps do not get this release at all.
If a browser client started getting 403 this week, what does your APP_URL actually say?