# Cross-Origin-Resource-Policy

> Learn how Cross-Origin-Resource-Policy (CORP) blocks no-cors cross-origin requests to protect resources from Spectre attacks and data leaks.

Source: https://howhttpworks.com/headers/cross-origin-resource-policy
Last reviewed: 2026-10-05

> **TL;DR:** `Cross-Origin-Resource-Policy` (CORP) tells the browser which sites may embed a response through a plain `no-cors` load, like an `<img>` or classic `<script>` tag. `same-origin` blocks other origins from using it. CORP isn't access control: the request still reaches your server. Mark public assets `cross-origin` so pages that enforce COEP can still embed them.

## What is Cross-Origin-Resource-Policy?

CORP is a response header that the browser checks on no-cors resource loads. If the policy doesn't allow the embedding page, the browser withholds the response data from it. By then the request has usually reached your server already. [MDN explains that the protection acts on the response body](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CORP).

## Why CORP Exists

JavaScript can't read an opaque no-cors response, but the browser will happily use it as an image or a script. That's enough exposure to matter for side-channel attacks such as Spectre. CORP gives the resource owner a way to say who may use the response that way. Private resources still need real authentication on top.

## Syntax

```http
Cross-Origin-Resource-Policy: same-origin
```

Send exactly one value: `same-origin`, `same-site` or `cross-origin`. Values are case-sensitive. A comma-separated list doesn't work as an allowlist, and duplicate headers can make the browser drop the restriction entirely. [Fetch defines parsing and checks](https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header).

## Value Guide

`same-origin` requires a matching scheme, host and port. `same-site` allows any origin on the same site, with one transport restriction covered below. `cross-origin` lets any origin pass the CORP check. It doesn't give JavaScript CORS access to the response.

Under `same-site`, an HTTP page can't use an HTTPS resource, even when both hosts share a registrable domain. Meanwhile, two HTTPS hosts like `app.example.com` and `static.example.com` are different origins but the same site.

## Example

A public asset's response headers (trimmed example):

```http
Content-Type: image/png
Cross-Origin-Resource-Policy: cross-origin
```

A page enforcing COEP `require-corp` can load this image with a no-cors request and needs no CORS permission to do it. Add `crossorigin="anonymous"` to the `<img>` and the request switches to CORS mode, which then needs CORS permission.

## Practical Deployment Pattern

Set the policy where each resource is served, so same-origin-only assets and public CDN assets each get the right value. A blanket rule is how mistakes happen: put `same-origin` on a public image CDN and you block every no-cors consumer on another origin, including your own application host.

## Implementation

In nginx, add the directive to the asset's location:

```nginx
location /public-assets/ {
    add_header Cross-Origin-Resource-Policy "cross-origin" always;
}
```

This assumes your existing server already serves the files at that location. [nginx documents header contexts and inheritance](https://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header). Make sure no backend or middleware adds a second CORP value.

In an existing Express app, set the policy before the static handler:

```javascript
const express = require('express')
app.use('/public-assets', (_req, res, next) => {
  res.set('Cross-Origin-Resource-Policy', 'cross-origin')
  next()
}, express.static('/srv/app/public-assets'))
```

The directory is a fixed server path that requests can't change. [express.static](https://expressjs.com/en/5x/api/express/#express.static) serves the files, and the middleware in front marks them as usable cross-origin. Keep private downloads out of this directory and give them their own authentication and policy.

## CORP vs CORS

`CORP: same-origin` won't block a CORS-mode fetch that passes its CORS checks. In the other direction, `CORP: cross-origin` won't make a fetch response readable without `Access-Control-Allow-Origin`. Check which mode the request used before deciding the two headers conflict.

## Common Mistakes

The most common one is reaching for CORP to stop other sites framing your pages. Ordinary iframe navigations aren't the no-cors asset loads CORP covers. To control who can frame a document, use [CSP frame-ancestors](https://howhttpworks.com/headers/content-security-policy) or [X-Frame-Options](https://howhttpworks.com/headers/x-frame-options). COEP has its own separate checks for nested documents.

## CORP vs CORS: Different Problems, Different Solutions

Test in a real embedding context, not just by loading the URL directly:

```html
<img src="https://static.example.com/public-assets/logo.png" alt="Logo">
```

Serve that test page from a different origin and watch the Network and Console panels. curl can confirm the server returned data and the header, but only the browser tells you whether it allowed the embed:

```bash
curl -sS -D - -o /dev/null \
  https://static.example.com/public-assets/logo.png
```

CORP can't name a list of partner origins. You get three choices: same origin, same site, or anyone. If partners need JavaScript access to the data, configure CORS for that separately.

## Related Headers

- [Cross-Origin-Embedder-Policy](https://howhttpworks.com/headers/cross-origin-embedder-policy)
- [Cross-Origin-Opener-Policy](https://howhttpworks.com/headers/cross-origin-opener-policy)
- [Access-Control-Allow-Origin](https://howhttpworks.com/headers/access-control-allow-origin)
