Why does my PHP app show a Content Security Policy error on Shopify Charge Confirmation page?

Topic summary

Issue: An embedded Shopify app (PHP/Symfony + React) attempts to redirect to the RecurringApplicationCharge confirmation page when no active charge exists, but shows a blank screen and a browser error citing Content Security Policy (CSP) frame-ancestors blocking admin pages from being iframed.

Key details:

  • App flow: Admin → app loads in iframe → app responds with 302 to charge confirmation → browser blocks with CSP error (“Refused to load … does not appear in the frame-ancestors directive”).
  • CSP: A security policy that restricts what can be framed; Shopify admin billing/ auth pages cannot be displayed inside the embedded iframe.

Proposed approach:

  • Initiate billing via a full-page (top-level) redirect instead of an iframe redirect.
  • Frontend requests backend to create the charge, backend returns the confirmation URL, frontend sets window.top.location.href to that URL (code snippet provided).

Recent update/complication:

  • Another user reports that attempting window.top.location.href from the embedded context triggers a browser error: unsafe top-level navigation without a user gesture and cross-origin restrictions, resulting in a DOMException.

Status:

  • No confirmed resolution yet. Open question: how to reliably navigate from an embedded app to the charge confirmation page without violating CSP or cross-origin/navigation gesture requirements.
Summarized with AI on January 4. AI used: gpt-5.

I am creating a new app using PHP (Symfony) and React, taking inspiration from the Laravel APP located at https://github.com/Shopify/shopify-app-template-php. However, I am facing a specific issue and do not have any idea what the problem might be.

When I click on the URL in the console after calling npm run dev, everything works great and I am redirected to the Shopify Charge Confirmation page. However, the problem occurs when I open the app in the Admin panel, and the current installation does not have an active charge. Then I notice a strange behavior:

  1. I click on the app name to open it.
  2. I see a request to my app in the console: https://xxx.ngrok.io/?embedded=1&hmac=…&host=…&locale=en-US&session=…&shop=…&timestamp=1680259371
  3. In the response, I can see a redirect 302 to the charge page: https://xxx.com/admin/charges/X/Y/RecurringApplicationCharge/confirm_recurring_application_charge?signature=
  4. I can see an empty page (my app is not loaded) and an error in the console: “Refused to load https://xyz.myshopify.com/admin/auth/login because it does not appear in the frame-ancestors directive of the Content Security Policy.”

I don’t have any idea how to fix this issue.

The issue you’re experiencing is related to Shopify’s Content Security Policy (CSP). When you try to load the charge confirmation page within the Shopify admin panel, it’s being blocked due to the CSP restrictions.

To fix this issue, you should handle the billing process in a way that it opens in a new window or a full-page redirect, rather than within the embedded app frame.

When the app is loaded and you detect that there’s no active charge, send a message from the app frontend (React) to the backend (PHP Symfony) to initiate the billing process.

In your PHP Symfony backend, create a route that handles the billing process. This route should redirect the user to the charge confirmation page with a full-page redirect, instead of returning a 302 redirect.

In your React frontend, listen for the response from the backend and use the window.top.location.href property to perform the full-page redirect. This will open the charge confirmation page in the parent window, bypassing the CSP restrictions.

// Receive the response from your backend containing the charge confirmation URL
const chargeConfirmationUrl = response.data.url;

// Perform the full-page redirect
window.top.location.href = chargeConfirmationUrl;

You can avoid the CSP issue with this approach and allow users to see the charge confirmation page without any errors.

2 Likes

thanks for you help.

Hello, @okur90

Thank you for that but unfortunately, I’m getting an error trying to redirect to a charge confirmation URL from an embedded app.

Do you have any ideas on that?

Unsafe attempt to initiate navigation for frame with origin ‘https://admin.shopify.com’ from frame with URL ‘https://xxx.herokuapp.com/?embedded=1&hmac=123&host=abc&id_token=qwe..-&locale=en-US&session=123&shop=test.myshopify.com&timestamp=1708550546’. The frame attempting navigation is targeting its top-level window, but is neither same-origin with its target nor has it received a user gesture. See https://www.chromestatus.com/feature/5851021045661696.

Uncaught (in promise) DOMException: Failed to set the ‘href’ property on ‘Location’: The current window does not have permission to navigate the target frame to ‘https://test.myshopify.com/admin/charges/123/123/RecurringApplicationCharge/confirm_recurring_application_charge?signature=abc’.