packages/cop-middleware/README.md
Cross-origin protection middleware for Remix. It mirrors Go's CrossOriginProtection by rejecting unsafe cross-origin browser requests without synchronizer tokens.
Sec-Fetch-Site when present and falls back to Originnpm i remix
import { createRouter } from 'remix/fetch-router'
import { cop } from 'remix/cop-middleware'
let router = createRouter({
middleware: [cop()],
})
For unsafe methods (POST, PUT, PATCH, DELETE), cop() follows the same broad model as Go's CrossOriginProtection:
Sec-Fetch-Site: same-originSec-Fetch-Site: noneSec-Fetch-Site values unless the request matches a trusted origin or insecure bypassSec-Fetch-Site is missing, compare Origin to the request hostSec-Fetch-Site and Origin are missing, allow the requestThis middleware is intentionally tokenless. If you cannot guarantee the deployment assumptions behind that model, prefer csrf-middleware.
cop() is a browser-origin guard, not a universal CSRF solution. It is designed for deployments that can rely on modern browser provenance signals and same-origin request handling.Sec-Fetch-Site and Origin are missing on an unsafe request, cop() allows the request to continue. This is intentional so older clients and non-browser callers do not fail closed by default.Sec-Fetch-Site is missing, cop() only rejects when Origin is present and does not match the request host.csrf-middleware or layer both middlewares together.You can also layer cop() in front of csrf() when you want both browser provenance checks and session-backed synchronizer tokens.
import { createCookie } from 'remix/cookie'
import { createRouter } from 'remix/fetch-router'
import { createCookieSessionStorage } from 'remix/session/cookie-storage'
import { session } from 'remix/session-middleware'
import { cop } from 'remix/cop-middleware'
import { csrf } from 'remix/csrf-middleware'
let sessionCookie = createCookie('__session', { secrets: ['secret1'] })
let sessionStorage = createCookieSessionStorage()
let router = createRouter({
middleware: [cop(), session(sessionCookie, sessionStorage), csrf()],
})
In this setup, cop() runs first and rejects unsafe cross-origin browser requests early using Sec-Fetch-Site and Origin. Requests that pass cop() continue into csrf(), which still enforces synchronizer-token validation and origin checks for the remaining traffic.
import { createRouter } from 'remix/fetch-router'
import { cop } from 'remix/cop-middleware'
let router = createRouter({
middleware: [
cop({
trustedOrigins: ['https://admin.example.com'],
}),
],
})
Trusted origins must be exact origin values in the form scheme://host[:port].
Bypass patterns intentionally weaken protection for specific endpoints. They support:
POST /webhooks/{provider}/healthz/webhooks/{name}{name...}import { createRouter } from 'remix/fetch-router'
import { cop } from 'remix/cop-middleware'
let router = createRouter({
middleware: [
cop({
insecureBypassPatterns: ['POST /webhooks/{provider}', '/healthz'],
}),
],
})
csrf-middleware - Session-backed CSRF protection with synchronizer tokensfetch-router - Router for the web Fetch APISee LICENSE