cop-middleware
The cop middleware provides tokenless cross-origin protection by inspecting the browser's Sec-Fetch-* headers. These headers are automatically sent by modern browsers and cannot be spoofed by JavaScript, making them a reliable signal for detecting unwanted cross-origin requests.
Installation
The COP middleware is included with Remix. No additional installation is needed.
Import
import { cop } from 'remix/cop-middleware'API
cop(options?)
Returns a middleware function that validates requests using Sec-Fetch-* headers and sets cross-origin isolation response headers.
let router = createRouter({
middleware: [
cop(),
],
})Options
| Option | Type | Default | Description |
|---|---|---|---|
allowedSites | string[] | ['same-origin'] | Allowed values for the Sec-Fetch-Site header. Options: 'same-origin', 'same-site', 'cross-site', 'none'. |
allowedModes | string[] | ['navigate', 'same-origin', 'no-cors'] | Allowed values for the Sec-Fetch-Mode header. |
allowedDests | string[] | ['document', 'empty'] | Allowed values for the Sec-Fetch-Dest header. |
reportOnly | boolean | false | When true, logs violations but does not block requests. Useful for testing before enforcing. |
How It Works
Modern browsers send Sec-Fetch-* headers with every request. These headers describe the context of the request:
| Header | Description | Example Values |
|---|---|---|
Sec-Fetch-Site | Relationship between request origin and target | same-origin, same-site, cross-site, none |
Sec-Fetch-Mode | The mode of the request | navigate, cors, no-cors, same-origin |
Sec-Fetch-Dest | The intended destination of the request | document, script, image, empty |
The middleware:
- Checks if the request includes
Sec-Fetch-*headers (browsers that support them always send them). - Validates the headers against the configured allowed values.
- Blocks requests that do not match with a
403 Forbiddenresponse. - Sets cross-origin isolation response headers:
Cross-Origin-Opener-Policy,Cross-Origin-Embedder-Policy, andCross-Origin-Resource-Policy.
Requests without Sec-Fetch-* headers (such as those from non-browser clients like curl) are allowed through by default, since these clients are not vulnerable to cross-origin attacks.
When to Use COP vs CSRF vs CORS
| Middleware | Purpose | Token Required | Best For |
|---|---|---|---|
cop() | Block cross-origin requests using browser headers | No | Server-rendered apps where all requests come from same origin |
csrf() | Validate that form submissions originate from your site | Yes (hidden field or header) | Apps with HTML forms and session-based auth |
cors() | Allow specific cross-origin requests | No | APIs consumed by other domains |
COP and CSRF solve similar problems (preventing cross-origin state changes) but use different mechanisms. COP is simpler because it requires no token management, but it only works in browsers that support Sec-Fetch-* headers (all modern browsers do). CSRF tokens work in all browsers.
You can use both together for defense in depth:
let router = createRouter({
middleware: [
cop(), // First line of defense: block cross-origin requests
csrf(), // Second line: validate tokens for older browsers
],
})Examples
Basic Usage
import { createRouter } from 'remix/fetch-router'
import { cop } from 'remix/cop-middleware'
let router = createRouter({
middleware: [
cop(),
],
})Allow Same-Site Requests
If your app has subdomains that need to make requests to each other:
cop({
allowedSites: ['same-origin', 'same-site'],
})Report-Only Mode
Test COP without blocking requests:
cop({
reportOnly: true,
})Violations are logged to the console, so you can identify which requests would be blocked before enforcing.
Allow Navigation and API Calls
cop({
allowedModes: ['navigate', 'same-origin', 'cors'],
allowedDests: ['document', 'empty', 'script', 'style'],
})Related
- Middleware --- How middleware works in Remix.
- Security Guide --- Cross-origin protection in the context of application security.
- csrf-middleware --- Token-based CSRF protection.
- cors-middleware --- Allowing specific cross-origin requests.