Clickjacking headers for shopify app

Topic summary

Main issue: Apps are being rejected in review for missing Content-Security-Policy with a frame-ancestors directive that prevents clickjacking. Required value: frame-ancestors https://[shop].myshopify.com https://admin.shopify.com, where [shop] is the current shop domain.

Key clarifications: The header must be present on every response where the app can be framed, including install/auth and post-install pages. It must use the dynamic shop value, not a wildcard.

Tried solutions:

  • Next.js next.config.js static headers fail due to the dynamic [shop] value.
  • Server middleware (Koa/Express) sets the header per request using ctx.query.shop or a cookie (shopOrigin). Some used koa-helmet to generate CSP. Concerns were raised about third‑party cookies, but one report says this still passed review.
  • Alternatives: set header in appRouter for all routes, or in getServerSideProps on index page. PHP implementations set the header on every page.

Recent update: The newest Shopify CLI project template includes built-in middleware that sets the correct CSP for embedded apps (uses shop from query; otherwise sets frame-ancestors ‘none’).

Current status: Mixed results. Some passed review with the middleware approaches; others still face rejections and seek concrete failing URLs. Code snippets and one screenshot are central. Discussion remains open.

Summarized with AI on January 31. AI used: gpt-5.

Hi there,

Recently I attempted to submit an alternative payment gateway to the shopify store and was almost immediately rejected with the following:

App must set security headers to protect against clickjacking.
Your app must set the proper frame-ancestors content security policy directive to avoid clickjacking attacks. The ‘content-security-policy’ header should set frame-ancestors https://[shop].myshopify.com https://admin.shopify.com, where [shop] is the shop domain the app is embedded on.

And I’m wondering - do these headers have to be present on the installation of the app?

Or are we expected to set them upon redirect to the app for the alternative payment gateway flow. Just wondering where in the order of operations these need to exist.

2 Likes

Hi did you manage to resolve this issue ?

Hi

Is it OK to display this in the response header?

content-security-policy frame-ancestors https://*.myshopify.com https://admin.shopify.com

1 Like

how can i resolve this issue because that my app was rejected constantly PLz Guaid

  1. The app must set security headers to protect against clickjacking.
    Your app must set the proper frame-ancestors content security policy directive to avoid clickjacking attacks. The ‘content-security-policy’ header should set frame-ancestors HTTPS://[shop].myshopify.com https://admin.shopify.com, where [shop] is the shop domain the app is embedded on.
  2. where I have put this code in an over the app

I don’t quite understand what you mean. Can you explain it more clearly? Thanks.

Thanks for Replay
Actually I have made a public app on Shopify partner, when I send that app for review, then this error is there. So do you have any idea about this error?

I am creating a public app and when it was sent for review this is the error which we are facing right now. Can you please help me in resolving it thanks.

The Error:

App must set security headers to protect against clickjacking.
Your app must set the proper frame-ancestors content security policy directive to avoid clickjacking attacks. The ‘content-security-policy’ header should set frame-ancestors https://[shop].myshopify.com https://admin.shopify.com, where [shop] is the shop domain the app is embedded on.

And I’m wondering - do these headers have to be present on the installation of the app?

Or are we expected to set them upon redirect to the app for the alternative payment gateway flow. Just wondering where in the order of operations these need to exist.

1 Like

Hi

I had the same issue and what solved that error for me was adding the headers in the next.config file, I did it exatcly using the NextJS documentation here

Where the key is “Content-Security-Policy” and the value is “frame-ancestors https://[shop].myshopify.com https://admin.shopify.com/;”

1 Like

I have tried the link you shared Luckily I am using NextJs but still My account got rejected after review with same issue. What should I do now ?

I have a question. How did you pass the value for [shop] to the headers function in next.config.js,
because it’s should be something like this: "frame-ancestors https://example-shop.myshopify.com https://admin.shopify.com/;

2 Likes

I have used the value as mentioned in the link.

Can you share an example of how you manage to create it ?

Thanks in advance

I also have these issue and I ensure I have add the header via PHP on EVERY page

header(“Content-Security-Policy: frame-ancestors https://”.$_GET[‘shop’]." https://admin.shopify.com;");

I thought there is some bug of Shopify automate test system

I have passed the information exactly the same way. Still same issue What should i do now please help

we are same boat, I wonder this forum has no official support reply in a week…

But your issue was resolved right ?

Hey can you do me a favor like if you can share your coding with me regarding this issue only.

This would mean alot.

Thanks

still reject from Shopify, but the email don’t specific which page with url encounter the problem, it quite bad dev experience.

I 100% sure I add the following line on every page and using F12 dev tool to check the header is exist.

to add the header is simple add

header(“Content-Security-Policy: frame-ancestors https://”.$_GET[‘shop’]." https://admin.shopify.com;");
and resulting

Content-Security-Policy: frame-ancestors https://abcshop.myshopify.com https://admin.shopify.com;

it really bad

1 Like

OK Thanks

I will update you after checking

I don’t know if this will help you, but this is how I tried to solve the problem (I’m still waiting for the review of my app):

I assume you also use React.js if you have the next.config.js file in your project.

In server.js I have defined a new function:

// Defense against clickjacking: https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html
  // https://shopify.dev/apps/store/security/iframe-protection
  // Only allow Shop host url to display site in iFrame
  const setClickJackingHeadersMiddleware = async (ctx, next) => {
    const shop = ctx.query.shop;
    try {
      if (shop) {
        ctx.set({
          "Content-Security-Policy": `frame-ancestors https://${shop} https://admin.shopify.com;`,
        });
        console.log(
          "Set Content-Security-Policy to: ",
          `frame-ancestors https://${shop} https://admin.shopify.com;`
        );
      } else {
      }
    } catch (err) {
      console.log("Doesnt matter");
    } finally {
      return next();
    }
  };

And this function is called by the router before any other router is defined:

router.use(setClickJackingHeadersMiddleware);

I will keep you updated if it has worked.
I could also create a gist on Github so you can see my complete server.js file if you want to.

4 Likes

Hi guys!

I have found a solution that passed the review!

I coudn’t find a way to insert a variable in the next.config file, if that variable wasnt defined in the dotenv file.

Now I’ll share a screen shot of how did I do it. (I am using a project generated with the Shopify CLI)

In server.js

Hopefully this help you out guys!

2 Likes

Ok will get to you on this one.

you were using Next.js right ?