# Cross-Origin-Resource-Policy (/en/docs/web-security/security-headers/cross-origin-resource-policy)



Everything your server publishes is embeddable by default. Any page on the web
can point an `<img>`, `<script>`, or `<video>` tag at your URLs and the browser
fetches the resource straight into that page's process.
`Cross-Origin-Resource-Policy` (CORP) is a response header a resource sends to
say who may load it: only its own origin, only its own site, or anyone.

It is the inverse of
[Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS):
CORS lets a server opt in to other origins reading its responses, while CORP
lets a resource opt out of being embedded by them.

The strictest setting keeps a resource for the origin that served it:

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

## Values and what each does [#values-and-what-each-does]

| Value          | Status | Description                                                                                                                                     |
| -------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `same-origin`  | ✅ Good | Only requests from the exact same origin (scheme, host, and port) receive the resource. The default-deny choice for private assets.             |
| `same-site`    | ✅ Good | Requests from the same registrable domain pass, subdomains included, with one scheme rule: an HTTP page cannot load an HTTPS resource this way. |
| `cross-origin` | ✅ Good | Any origin may load the resource. For truly public assets only; it exists mainly so pages behind COEP `require-corp` can still load them.       |

A missing header behaves like `cross-origin`: the resource stays loadable by
everyone. So does a value the browser cannot parse. Anything other than these
three keywords is treated as if no header were sent, so a typo silently turns
the protection off.

CORP does not stop the request. The browser sends it, your server answers, and
only then does the browser check the header and, on a mismatch, withhold the
body and fail the load with a network error. The resource never enters the
embedding page's process, but the request still reached your server.

The check applies only to no-cors requests, the default mode for `<img>`,
`<script src>`, `<audio>`, `<video>`, and stylesheets loaded without a
`crossorigin` attribute. A request made in CORS mode skips the CORP check,
because CORS already requires an explicit grant from the server. Requests from
the resource's own origin always pass, whatever the value. And CORP does not
block top-level navigations or plain `<iframe>` embeds on its own; iframe
responses are CORP-checked only when the embedding document enforces COEP
`require-corp`.

## What it protects against [#what-it-protects-against]

* **Spectre-class side channels.** CORP was revived in 2018, from the older
  From-Origin proposal, in response to Spectre and Meltdown. Those attacks let
  a malicious page read any data that shares its process, including
  cross-origin resources it embedded. CORP keeps your resource out of the
  attacker's process in the first place, so there is nothing to read.
* **Cross-site script inclusion (XSSI).** A script marked `same-origin` or
  `same-site` cannot be pulled in by another site's `<script>` tag, closing
  off attacks that include your scripts to read the data they carry.
* **Hotlinking.** Supporting browsers refuse to render your images and media
  on other sites. Note the limit: non-browser clients ignore the header, so
  this is embedding control, not bandwidth protection.

Browsers already enforce a default floor here. Opaque Response Blocking (ORB)
blocks no-cors reads of clearly-data responses (HTML, JSON, XML, PDFs,
archives) with no header needed, but it deliberately lets JavaScript, CSS,
images, and media through so existing sites keep working. The
[ORB explainer](https://github.com/annevk/orb) strongly encourages protecting
those pass-through resource types with `Cross-Origin-Resource-Policy` when
they contain anything sensitive. Treat ORB as the default and CORP as the
explicit control for what it lets through.

## The COEP connection [#the-coep-connection]

[Cross-Origin-Embedder-Policy (COEP)](/en/docs/web-security/policies/cross-origin-embedder-policy)
is the other half of the pair, and its own page covers it in full. A document
that enforces `Cross-Origin-Embedder-Policy: require-corp` only loads
cross-origin subresources that opt in, and it treats a missing or malformed
CORP header as `same-origin`. That flipped default is what blocks unlabeled
third-party assets, and it is why any host serving genuinely public assets (a
CDN, a widget, a font service) must send
`Cross-Origin-Resource-Policy: cross-origin` to stay embeddable in isolated
contexts.

One gotcha catches many rollouts: adding `Access-Control-Allow-Origin` to a
no-cors response does not satisfy `require-corp`. Either the embedding element
makes a real CORS request (a `crossorigin` attribute), or the resource sends
CORP. COEP's `credentialless` value is the alternative that loads CORP-less
cross-origin resources by stripping credentials from the request.

## Risks without it [#risks-without-it]

Any site can pull your assets into its own process. A page you have never
heard of can embed the avatars, charts, or documents your server returns for
logged-in users, then use a speculative-execution side channel to read them.
It can include your scripts with a `<script>` tag and observe what they expose
(XSSI). And it can hotlink your images and media, serving your content in its
own pages. ORB blocks only the clearly-data cases; everything else rides on
the `cross-origin` default until you send the header.

## Risks and gotchas when using it [#risks-and-gotchas-when-using-it]

* **`same-origin` breaks multi-subdomain setups.** Assets served from
  `cdn.example.com` and consumed by `www.example.com` are cross-origin to each
  other, so `same-origin` blocks them. Use `same-site` when subdomains share
  assets.
* **The HTTPS edge case in `same-site`.** The requester's scheme matters: an
  HTTP page cannot load an HTTPS resource marked `same-site`, even on the same
  registrable domain. This is the specified and implemented behavior, even
  though some documentation prose omits it.
* **Overmarking public assets breaks consumers.** Setting `same-origin` or
  `same-site` on assets other sites are meant to load breaks every third-party
  consumer, including any page that enforces COEP `require-corp`. Those assets
  need `cross-origin`.
* **Safe to deploy everywhere.** Browsers without CORP support ignore the
  header, so shipping it carries no compatibility risk.
* **It pairs well with server-side isolation.** CORP is enforced by the
  browser after the response arrives. Rejecting unwanted requests on the
  server with [Fetch Metadata](https://web.dev/articles/fetch-metadata)
  (for example, refusing `Sec-Fetch-Site: cross-site`) complements it.

## How to set it up [#how-to-set-it-up]

1. Classify what you serve into three buckets: private assets used by one
   origin, assets shared across your subdomains, and public assets meant for
   other sites.
2. Set the header per bucket: `same-origin` on private assets, `same-site` on
   subdomain-shared assets, `cross-origin` only on the public ones.
3. If any of your pages will enable COEP `require-corp`, load them with the
   new headers in place and confirm every subresource still arrives; a
   resource you overmarked in step 2 fails there first.
4. Check the deployed header, and the rest of your response headers, with the
   [security headers scanner](/tools/security-headers).

## Recommendation [#recommendation]

Send `Cross-Origin-Resource-Policy: same-site` as the default for your own
assets:

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

The [OWASP HTTP Headers cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html)
recommends `same-site`, limiting resource loading to your site and its
subdomains. Treat it as the ceiling, not the floor: prefer `same-origin`
wherever subdomains do not need the asset, and relax to `cross-origin` only on
assets other sites are meant to load, including COEP-isolated ones.

## Browser support [#browser-support]

Supported across current Chrome, Edge, Firefox, and Safari. Browsers without
support ignore the header, so there is no downside to sending it everywhere.

## FAQ [#faq]

### What is the difference between CORP and CORS? [#what-is-the-difference-between-corp-and-cors]

They are inverses. CORS is opt-in: a resource uses it to let other origins read
its content. CORP is opt-out: a resource uses it to declare who may embed it
cross-origin. CORP only governs no-cors requests, the default mode of images,
scripts, and media, while CORS-mode requests skip the CORP check entirely.

### What is the difference between CORP and COEP? [#what-is-the-difference-between-corp-and-coep]

CORP is set by the resource and says who may load it. COEP is set by the
embedding page and says it will only load resources that opt in. A missing CORP
header is treated as `same-origin` under COEP `require-corp`, which is what
blocks unlabeled third-party assets. See
[CORP vs COEP](/en/blog/corp-vs-coep) for the full comparison.

### Does CORP block hotlinking? [#does-corp-block-hotlinking]

In browsers, yes. A cross-origin no-cors load of a resource marked
`same-origin` or `same-site` fails with a network error, so another site cannot
embed your image or script. Non-browser clients ignore the header, though, so
treat CORP as embedding control against browsers, not as bandwidth or theft
protection.

## See also [#see-also]

* [Security headers overview](/en/docs/web-security/security-headers)
* [Cross-Origin-Embedder-Policy (COEP)](/en/docs/web-security/policies/cross-origin-embedder-policy),
  the page-side policy that makes CORP opt-in mandatory
* [Cross-Origin-Opener-Policy (COOP)](/en/docs/web-security/policies/cross-origin-opener-policy),
  the other half of cross-origin isolation
* [Security headers scanner](/tools/security-headers) to check your
  deployed headers

## Sources [#sources]

* [Fetch standard, Cross-Origin-Resource-Policy header](https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header)
* [MDN, Cross-Origin-Resource-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Resource-Policy)
* [MDN, Cross-Origin Resource Policy guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cross-Origin_Resource_Policy)
* [web.dev, COOP and COEP](https://web.dev/articles/coop-coep)
* [OWASP HTTP Headers cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html)
* [ORB explainer](https://github.com/annevk/orb)
