Solved

CORS error on assets using Shopify App Proxy / Remix app

rhatton
Shopify Partner
4 1 5

I am developing a private Shopify App using the Remix template

 

I have set up and created an App Proxy so that my app can be accessed via my merchant's store. I am able to receive requests and have managed to calculate and verify the HMAC signature.

 

Initially I had a problem where the browser was trying to access the build directory assets via the base URL of the merchant store and getting 404 errors. e.g.

 

https://merchant-store.com/build/ 

 

I corrected this by setting the 'publicPath' variable in my remix.config.js file to my public server

 

 

// SHOPIFY_APP_URL will contain the cloudflare tunnel url in dev and the live url on prod
const basePath = process.env.SHOPIFY_APP_URL || 'https://my-app-link.com';

/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
    ignoredRouteFiles: ['**/.*'],
    appDirectory: 'app',
    assetsBuildDirectory: `public/build/`,
    publicPath: `${basePath}/build/`,
    serverModuleFormat: 'cjs',
    dev: { port: process.env.HMR_SERVER_PORT || 8002 },
    future: {},
};

 

 

This works to some extent in that my requests via the app proxy are correctly resolving to my app location.

 

My issue is that this requests are blocked because of CORS as the original request is coming from the merchant store e.g. https://merchant-store.com/tools/sub-path-prefix/ 

 

Screenshot 2024-03-06 at 11.50.40.png

 

I been trying to work out how to allow CORS on these static assets, but I can't for the life of me work out how to do this. One post I found suggested to using express as the server adapter instead of the default, but I couldn't get this to work with the Shopify CLI. 

 

Any guidance would be greatly appreciated as I'm really struggling with this! I am publishing to fly.io using this tutorial if this helps.

Accepted Solution (1)

rhatton
Shopify Partner
4 1 5

This is an accepted solution.

After much frustration I managed to resolve this issue. I'll post my process in the hope that it can help somebody in future. 

 

As before, I set the 'publicPath' in the remix.config.js file to be my public endpoint path 

 

 

// SHOPIFY_APP_URL will contain the cloudflare tunnel url in dev and the live url on prod
const basePath = process.env.SHOPIFY_APP_URL || 'https://my-fly-io-public-endpoint.dev';

/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
    ignoredRouteFiles: ['**/.*'],
    appDirectory: 'app',
    publicPath: `${basePath}/build/`,
    serverModuleFormat: 'cjs',
    dev: { port: process.env.HMR_SERVER_PORT || 8002 },
    future: {},
};

 

 

I tried to use an express.js server adapter for Remix so I could set the CORS headers on the static assets, but this was proving cumbersome.

 

In the end, I discovered I could set a response header directly on my fly.io instance by adding the following lines to my fly.toml file. Maybe other hosting providers have something similar.

 

[http_service.http_options.response.headers]
  Access-Control-Allow-Origin = "*"

 

 

This resolved my 404/CORS errors, but this brought another error. A refresh loop with remix saying that the initial URL didn't match the URL at the time of hydration.

 

Initial URL (/account/manage/) does not match URL at time of hydration (/tools/account), reloading page...

 

I changed the name of my remix route to be the same as my shopify.

 

e.g https://merchant-store.com/tools/account  - https://my-fly-app.dev/tools/account

 

I was still getting a weird error:

Initial URL (/tools/account/) does not match URL at time of hydration (/tools/account), reloading page...

 

It seemed it was necessary to add the extra forward slash after the url or the refresh loop continued.

 

As a workaround I renamed my remix route to tools.account.$id.tsx to accept a dynamic segment and redirect if it wasn't present

 

 

//tools.account.$id.tsx

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
    // verify shopify HMAC signature...


    // if no dynamic segment ID provided, redirect and add another part of the URL segment
    if (!params.id) {
        return redirect('/tools/account/manage');
    }

    //... the rest of loader logic....
};

 

 Thankfully this is working now and I don't need to waste any more time on it.

View solution in original post

Replies 3 (3)

stefansweb2020
Shopify Partner
134 10 5

Did you set up in Shopify partners account right proxy URL?

Stefan Momcilovic
Founder of StefansWeb
https://stefansweb.com
rhatton
Shopify Partner
4 1 5

Hi Stefan, 

 

Thanks for the quick reply.

 

Yes, I did set up the correct URL. I am able to access my app through the proxy URL and see some static HTML on the page, it's just that the chunked .js files are not loaded as they are blocked by CORS.

rhatton
Shopify Partner
4 1 5

This is an accepted solution.

After much frustration I managed to resolve this issue. I'll post my process in the hope that it can help somebody in future. 

 

As before, I set the 'publicPath' in the remix.config.js file to be my public endpoint path 

 

 

// SHOPIFY_APP_URL will contain the cloudflare tunnel url in dev and the live url on prod
const basePath = process.env.SHOPIFY_APP_URL || 'https://my-fly-io-public-endpoint.dev';

/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
    ignoredRouteFiles: ['**/.*'],
    appDirectory: 'app',
    publicPath: `${basePath}/build/`,
    serverModuleFormat: 'cjs',
    dev: { port: process.env.HMR_SERVER_PORT || 8002 },
    future: {},
};

 

 

I tried to use an express.js server adapter for Remix so I could set the CORS headers on the static assets, but this was proving cumbersome.

 

In the end, I discovered I could set a response header directly on my fly.io instance by adding the following lines to my fly.toml file. Maybe other hosting providers have something similar.

 

[http_service.http_options.response.headers]
  Access-Control-Allow-Origin = "*"

 

 

This resolved my 404/CORS errors, but this brought another error. A refresh loop with remix saying that the initial URL didn't match the URL at the time of hydration.

 

Initial URL (/account/manage/) does not match URL at time of hydration (/tools/account), reloading page...

 

I changed the name of my remix route to be the same as my shopify.

 

e.g https://merchant-store.com/tools/account  - https://my-fly-app.dev/tools/account

 

I was still getting a weird error:

Initial URL (/tools/account/) does not match URL at time of hydration (/tools/account), reloading page...

 

It seemed it was necessary to add the extra forward slash after the url or the refresh loop continued.

 

As a workaround I renamed my remix route to tools.account.$id.tsx to accept a dynamic segment and redirect if it wasn't present

 

 

//tools.account.$id.tsx

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
    // verify shopify HMAC signature...


    // if no dynamic segment ID provided, redirect and add another part of the URL segment
    if (!params.id) {
        return redirect('/tools/account/manage');
    }

    //... the rest of loader logic....
};

 

 Thankfully this is working now and I don't need to waste any more time on it.