CSP frame-ancestors Restricts Page Embedding

Clickjacking does not require an attacker to replace the target application’s interface. A hostile page can place the real application inside a transparent or carefully positioned frame, then arrange decoy controls so a user’s click lands on an authenticated action in the framed page.

The frame-ancestors directive in Content Security Policy moves the embedding decision to the response being framed. The target declares which ancestors are acceptable. A conforming browser checks the ancestor chain before allowing the protected resource to load in a nested browsing context.

Content-Security-Policy: frame-ancestors 'none'

For a page that never needs to be embedded, 'none' establishes a compact boundary: no parent is accepted.

The framed response sets the boundary

frame-ancestors is evaluated for the resource that another document attempts to embed. It is not a rule about the frames that the protected page itself loads.

That distinction separates it from frame-src:

frame-ancestors -> who may embed this page
frame-src       -> which frame sources this page may load

A site can permit a payment provider in frame-src while still refusing to be framed by other sites through frame-ancestors. The two directives govern opposite sides of the relationship.

The policy can permit same-origin framing:

Content-Security-Policy: frame-ancestors 'self'

It can also name specific sources when legitimate cross-origin embedding is required:

Content-Security-Policy: frame-ancestors 'self' https://portal.example

An allowlist should correspond to an actual embedding requirement. Broadening it for convenience increases the set of contexts that can present the protected interface inside another page.

Every ancestor in a nested chain matters

A nested frame can have more than one ancestor. The browser checks the chain rather than accepting the load solely because the immediate parent matches.

Consider this structure:

https://outer.example
    |
    v
https://portal.example
    |
    v
https://app.example

If app.example allows only portal.example, the outer ancestor still matters. A nonmatching ancestor causes the protected load to be blocked.

This property prevents an allowed intermediary from automatically becoming a tunnel through which any unrelated top-level site can embed the target. Deployments with nested portals therefore need to account for the complete ancestor structure that browsers will see.

default-src does not supply a fallback

Many CSP fetch directives inherit from default-src when they are omitted. frame-ancestors does not.

This policy does not prohibit other sites from framing the response:

Content-Security-Policy: default-src 'none'

The embedding restriction must be stated explicitly:

Content-Security-Policy: default-src 'none'; frame-ancestors 'none'

Treating default-src 'none' as a universal deny rule can leave an unintended framing gap. CSP directive classes have distinct processing rules, so a restrictive fetch policy is not a substitute for an ancestor policy.

The directive belongs in an HTTP response header

CSP supports some policies through an HTML <meta http-equiv="Content-Security-Policy"> element, but frame-ancestors is not one of them. The CSP specification requires browsers to ignore this directive when it appears in a policy delivered through a meta element.

The effective form is an HTTP response header:

Content-Security-Policy: frame-ancestors 'none'

This placement also means the decision is available before the protected document’s markup could attempt to influence framing behavior. Application routes, reverse proxies, and CDN configuration should be checked so the header reaches every response that needs the boundary.

X-Frame-Options covers a smaller policy space

X-Frame-Options predates CSP and commonly uses DENY or SAMEORIGIN:

X-Frame-Options: DENY

Those values map roughly to common frame-ancestors cases:

X-Frame-Options: DENY
CSP: frame-ancestors 'none'

X-Frame-Options: SAMEORIGIN
CSP: frame-ancestors 'self'

frame-ancestors can additionally express selected host or scheme sources. Under CSP processing, an enforced policy containing frame-ancestors takes precedence over X-Frame-Options for user agents that implement the directive.

Some deployments send both headers for compatibility with older clients. The values should express compatible intent rather than establish two contradictory policies.

Framing policy is not an authorization check

Blocking unapproved framing removes a condition used by many UI-redressing attacks, but it does not authorize sensitive operations. A request that changes account state still needs the application’s normal authentication and authorization controls.

The directive also does not replace CSRF defenses. Clickjacking concerns a user interacting with a framed interface; CSRF concerns a site causing a browser to send an unwanted authenticated request. The attack paths can overlap around authenticated browser state, but the controls operate at different layers.

Cookie SameSite settings can reduce credential exposure in some cross-site embedding situations. They remain separate from the explicit decision about whether a page may be embedded.

Legitimate embedding needs a narrow contract

Applications sometimes require framing for administration consoles, partner portals, or composed interfaces. In those cases, 'none' is too restrictive, while unrestricted framing leaves the UI boundary open.

A narrow source list makes that dependency visible:

Content-Security-Policy: frame-ancestors https://console.example https://partner.example

The list should be maintained with the same care as other trust boundaries. A permitted origin can place the application inside its own document, so ownership and security of that origin are part of the framing model.

Changes to portal topology also deserve testing. Adding another wrapper frame can break a previously valid chain if the new ancestor is absent from the target policy.

Test the response, not only the configuration file

The security property exists in the header delivered to the browser. Configuration that appears correct at one layer can be lost on an error route, overwritten by a proxy, or omitted from a separate application surface.

Verification should inspect representative responses and exercise both accepted and rejected embedding paths. For nested framing, tests should include the complete ancestor chain rather than only a direct iframe.

A concise policy is useful only when it reaches the intended responses. frame-ancestors gives the protected page control over its embedding boundary; deployment work makes that boundary consistent across the application.

References