Allow embedding in Next.js
Add a Content-Security-Policy header with frame-ancestors in the headers() function of next.config.ts. If you already build a CSP in proxy.ts or middleware, add frame-ancestors to that policy instead.
4 min read · Updated
Find the current header
curl -sI https://your-site.com/ | grep -iE "x-frame-options|content-security-policy"
A framing header comes from your Next.js config, your code, or your host. Search the project:
grep -Rni "x-frame-options\|frame-ancestors" next.config.* proxy.* middleware.* vercel.json src/ app/ 2>/dev/null
Remove any X-Frame-Options entry you find. Security templates often copy X-Frame-Options: DENY into next.config.
Allow one partner site with headers()
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
async headers() {
return [
{
source: "/:path*",
headers: [
{
key: "Content-Security-Policy",
value: "frame-ancestors 'self' https://partner.example",
},
],
},
];
},
};
export default nextConfig;
If you already set a full CSP here, add frame-ancestors to that same string. Do not add a second Content-Security-Policy entry.
Allow framing on one path only
When two entries match a path and set the same key, the last entry wins. So put the general rule first and the embed rule last:
async headers() {
return [
{
source: "/:path*",
headers: [{ key: "Content-Security-Policy", value: "frame-ancestors 'self'" }],
},
{
source: "/embed/:path*",
headers: [{ key: "Content-Security-Policy", value: "frame-ancestors https://partner.example" }],
},
];
},
If you build the CSP in proxy.ts
Next.js 16 renamed middleware.ts to proxy.ts. If you make a nonce-based CSP there, add frame-ancestors to that policy:
// proxy.ts
import { NextResponse, type NextRequest } from "next/server";
export function proxy(request: NextRequest) {
const nonce = Buffer.from(crypto.randomUUID()).toString("base64");
const csp = [
"default-src 'self'",
`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`,
"frame-ancestors 'self' https://partner.example",
].join("; ");
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-nonce", nonce);
requestHeaders.set("Content-Security-Policy", csp);
const response = NextResponse.next({ request: { headers: requestHeaders } });
response.headers.set("Content-Security-Policy", csp);
return response;
}
On Next.js 15 or older, the file is middleware.ts and the function is middleware. Keep the CSP in proxy.ts or in next.config, not in both. Otherwise the page can get two policies.
Check the result
Run next build && next start, or deploy. Then run the curl command again. You want one CSP line with your frame-ancestors and no X-Frame-Options.
Common mistakes
output: "export". A static export does not supportheaders(). Set the header on the host that serves the files, for example in Netlify or Vercel config.- CSP in a
<meta>tag or inmetadata.frame-ancestorsonly works as a response header. - The host adds X-Frame-Options. A CDN or reverse proxy in front of Next.js can add it. Check the live URL, not only
localhost. ALLOW-FROM. Browsers ignore it.
Check the live page
Paste the deployed URL into the iframe tester. It shows if the page can load in a frame and explains why.
To check many pages at once, use the bulk check. It shows X-Frame-Options and frame-ancestors for up to 100 URLs.