CSP narrows the table before you do
A strict script-src, a nonce you cannot obtain at build time, or a frame-ancestors policy you are not listed in each remove options unilaterally — read the host’s response headers before you read their component library.
You have spent this module learning three mechanisms. A host can delete any of them with a response header they configured two years ago, for a reason unrelated to you, and they will not think to mention it, because from their side nothing about it is new information.
The delivery-constraints lesson put Content Security Policy first in the elimination order and worked three directives: frame-ancestors, script-src and connect-src. This lesson closes the module by pointing that filter at the three mechanisms you now know in detail, and by adding the directive nobody checks — the one that does not stop your component loading, only stop it looking like anything.
Point the directives at the mechanisms
Each mechanism in this module dies to a different directive, and knowing which lets you check the specific thing rather than reading a policy for general impressions.
| Mechanism | The directive that can delete it | What to look for |
|---|---|---|
| Iframe embed | frame-ancestors, on your origin | Whether the host is listed as a permitted parent |
| Module Federation remote | script-src, on the host | Whether your origin is allowed, and whether a nonce or 'strict-dynamic' is present |
| Orchestrated application | script-src, on the host | The same question, asked of whatever the root config’s load function fetches |
| Anything that talks to your API | connect-src, on the host | Whether your API origin is permitted from their page |
| Anything that injects a stylesheet | style-src, on the host | Whether inline styles are permitted, and by what — the directive below |
Two of those rows are worth stating out loud because they invert the intuition. frame-ancestors is served by you, not by the host: it is the header on your own document naming who may embed it, so an embed that fails is your configuration to fix. connect-src is served by them and governs your fetches: the embedding page’s policy applies to script interfaces used inside it, regardless of whose code is running.
Where people get burned
A <meta> tag cannot restrict who may embed a page. MDN states it plainly: frame-ancestors “is not supported in the <meta> element.” This is the specific trap that makes reading a repository the wrong method. You find a meta tag in their layout template, read the policy off it, and reach a conclusion the browser will never act on. Read the response headers of the deployed host.
The directive nobody budgets for: style-src
Every mechanism in this course except the iframe delivers styles into the host’s document, and almost all of them do it by injecting a <style> element or setting a style attribute at run time. That is what style-src governs, and MDN documents an asymmetry inside it that decides real designs:
- Injecting an inline
<style>element is blocked without'unsafe-inline'or a nonce. This is why CSS-in-JS needs nonce plumbing to run in a strict host, and why a component that builds its own stylesheet at run time can render completely unstyled with nothing in the network tab to explain it. - Setting a single property through the CSSOM —
element.style.color = 'red'— is explicitly not blocked. - Setting
element.style.cssTextis blocked.
One line of JavaScript works and the line beside it does not. If your component sets styles imperatively anywhere, that distinction is the difference between shipping and a bug nobody can reproduce locally, because a development server almost never carries the production policy.
How to get a policy changed
Sometimes the honest answer is that the mechanism you want needs one line added to their header. That is a negotiation, and it goes badly when you open with a request for permissiveness. Three things make it go well.
Ask for the narrowest possible amendment. A specific origin added to script-src is a change a security reviewer can evaluate in a minute. 'unsafe-inline' is a change that gets escalated, and rightly. Know which one you actually need — and remember that on a host whose policy already carries a nonce, asking for 'unsafe-inline' gets you nothing anyway, because a directive containing nonce or hash expressions makes the browser ignore that keyword entirely.
Offer report-only first. MDN’s CSP guide documents a report-only mode with a reporting endpoint, which lets a host deploy a policy change and watch what it would have broken without breaking anything. A team that has never met you will take a reversible, observable change long before an irreversible one.
Let them load you. On a host using 'strict-dynamic', allowlists and 'self' stop meaning anything and trust flows from a script the host itself nonced. That reads as a wall, and it is actually the door: no policy change is needed at all if their own nonced bootstrap loads your bundle. The delivery-constraints lesson covers that directive’s mechanics; the point to carry into the meeting is that “you load me” is a smaller ask than “change your policy.”
Check your recall
Answer from memory — no scrolling back.
Retrieval check
Name the four host facts you can establish about a delivery mechanism from response headers alone, without ever opening their repository.
Check your answer
Whether an iframe can be used at all, from frame-ancestors — though note this one is a header on your own origin, so the host’s policy tells you about their embedding, and yours decides whether they may embed you. Whether your bundle can load, from script-src: your origin allowed, and whether a nonce or 'strict-dynamic' means the host has to load you rather than you loading yourself.
Whether the surface can reach your API once running, from connect-src, which is the embedding page’s policy and not yours. And whether your styles can arrive, from style-src, which is the one that fails without an error anybody notices. Four facts, ten minutes, no access requested from anyone.
Hands on
Capture the headers and annotate the delivery table
Done when: ARTIFACT.md carries the raw Content-Security-Policy header for each host you are targeting, and /grafting-ui/reference/delivery-modes is annotated per host with which mechanisms that host’s policy eliminated and which directive did the eliminating. An entry with no directive named beside it does not count.
- For each host, fetch the deployed production page and capture the full
Content-Security-Policyheader verbatim. Usecurl -sIor the Network panel’s Headers tab, not a summary you typed from memory. CaptureContent-Security-Policy-Report-Onlytoo if it is present, because it tells you what they are about to enforce. - Also capture the header your own serving origin returns, specifically its
frame-ancestors. If you do not currently set one, write that down — it means the iframe option is open and that you have a decision to make about who else may embed you. - Work the table above for each host, one row at a time, and record a verdict per mechanism with the directive and the specific value that produced it. “Module Federation: blocked” is not a finding. “Module Federation: blocked —
script-srcis'self'plus a nonce, so our CDN origin cannot load and the nonce is per-request” is. - Test the constructed-stylesheet question empirically on one host. Load a page under their policy, build a
CSSStyleSheet, adopt it into a shadow root, and see whether it paints and whether the console reports a violation. Record what you observed and the date. You are producing evidence for a question the documentation does not answer. - Annotate the delivery-modes reference page with a per-host column of eliminations. This is the page you keep open in a real engagement, and its value comes entirely from carrying real header values rather than general properties of the mechanisms.
- Where a host’s policy blocks your preferred mechanism, draft the narrowest amendment that would unblock it, in one sentence a security reviewer could approve or reject on its own. Bring it into the chat and I will read it as that reviewer.
What this does not cover
This module has been about mechanisms and the headers that permit them. None of it makes a graft look like it belongs. A component that loads perfectly and renders in your own brand colours inside somebody else’s product is still visibly a foreign object, and fixing that by shipping ninety themes is the failure the next module exists to prevent.
The lesson on extracting the host brand starts there: read a documented set of the host’s custom properties, with your own values as fallbacks, so the graft inherits their brand without asking anyone to adopt your design system. The lesson on the session you inherit picks up the auth thread this module only touched at the iframe boundary.
Read this next — primary source
Content-Security-PolicyMDN Web Docs — fetched 2026-09-05. Free.
The header reference rather than the guide, because this lesson is about reading a real policy off a real host and the reference is the only page that lists every directive you might find in one. Read it once end to end so that an unfamiliar directive in a host’s header is a thing you look up rather than a thing you skip. Two sections earn a second pass: the fetch directives and what each one actually governs, and the note that a nonce in a directive makes unsafe-inline inert. Note also what this page is: MDN documenting the specification, not the specification itself.
Stuck, curious, or think this lesson is wrong? Ask your teaching agent. The lessons are the scaffold; the conversation is where the learning gets unstuck.