Skip to content

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

ts
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.

ts
let router = createRouter({
  middleware: [
    cop(),
  ],
})

Options

OptionTypeDefaultDescription
allowedSitesstring[]['same-origin']Allowed values for the Sec-Fetch-Site header. Options: 'same-origin', 'same-site', 'cross-site', 'none'.
allowedModesstring[]['navigate', 'same-origin', 'no-cors']Allowed values for the Sec-Fetch-Mode header.
allowedDestsstring[]['document', 'empty']Allowed values for the Sec-Fetch-Dest header.
reportOnlybooleanfalseWhen 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:

HeaderDescriptionExample Values
Sec-Fetch-SiteRelationship between request origin and targetsame-origin, same-site, cross-site, none
Sec-Fetch-ModeThe mode of the requestnavigate, cors, no-cors, same-origin
Sec-Fetch-DestThe intended destination of the requestdocument, script, image, empty

The middleware:

  1. Checks if the request includes Sec-Fetch-* headers (browsers that support them always send them).
  2. Validates the headers against the configured allowed values.
  3. Blocks requests that do not match with a 403 Forbidden response.
  4. Sets cross-origin isolation response headers: Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy, and Cross-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

MiddlewarePurposeToken RequiredBest For
cop()Block cross-origin requests using browser headersNoServer-rendered apps where all requests come from same origin
csrf()Validate that form submissions originate from your siteYes (hidden field or header)Apps with HTML forms and session-based auth
cors()Allow specific cross-origin requestsNoAPIs 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:

ts
let router = createRouter({
  middleware: [
    cop(),   // First line of defense: block cross-origin requests
    csrf(),  // Second line: validate tokens for older browsers
  ],
})

Examples

Basic Usage

ts
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:

ts
cop({
  allowedSites: ['same-origin', 'same-site'],
})

Report-Only Mode

Test COP without blocking requests:

ts
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

ts
cop({
  allowedModes: ['navigate', 'same-origin', 'cors'],
  allowedDests: ['document', 'empty', 'script', 'style'],
})

Released under the MIT License.