Re: Customer Events Pixel Access-Control-Allow-Origin Error

Solved

Customer Events Pixel Access-Control-Allow-Origin Error

mthdvd
Shopify Partner
26 1 10

Can anyone explain to me why I cannot seem to use a custom, customer events pixel to post data to an externally hosted server?

I have a simple script intended to post data to an external server:

 

analytics.subscribe('product_viewed', (event) => {
  const product_data = {
    attribute_one: “xxxxxxx”,
    attribute_two: “xxxxxxx”,
    data: event.data
  };
  fetch('https://mysecureendpoint.com/product_views, {     method: 'POST',     headers: {       'Content-Type': 'application/json'     },     body: JSON.stringify(product_data),     keepalive: false,   }) …. });


But I consistently get errors like:

 

Access to fetch at 'https://mysecureendpoint.com/product_views' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

If I make the same exact call outside of the customer events section, the request is successful and I receive no errors. No issues.

I'm using the same data, the same server & configurations, the same endpoint. The only difference is where the call takes place i.e. inside the customer events section vs directly on the product.liquid file.

This seems like a pretty obvious use case for the customer events utility. The external server, of course, is hosted on a different domain than the shopify store but is this intended behavior? What am I missing?

 

Thanks for any help!

Accepted Solution (1)

mthdvd
Shopify Partner
26 1 10

This is an accepted solution.

Okay, so here's the situation.

 

The iframe that loads in the Customer Event strips the request of it's origin. Specifically, the origin appears to always be set to "null."

It seems this is just the way the iframe is set up to work by Shopify -- even when 'credentials: include' or 'mode: cors' is explicitly set on the request. Why and for what purpose, I cannot say. Why Shopify omitted mention of this I also cannot say, as it does impact how you need to set up your CORS configuration within your app.

 

In my case, I'm running Rails using rack-cors. To get things to work, a wild card must be used for the endpoint.

 

For example if my endpoint is: https://mysecureendpoint.com/product_views, CORS must be configured like this:

 allow do 
  origins '*'
  resource '/product_views', headers: :any, methods: [:post, :options]
end


My issue is that I was trying to configure things like this:

 allow do 
  origins 'https://storeurl.com'
  resource '/product_views', headers: :any, methods: [:post, :options]
 end

 

The issue with the above configuration is that it explicitly sets an origin. Since the origin will always be null coming from the iframe, an error will be thrown.

 

For the sake of completion here are some other things to consider or try if you're running into problems.

 

  • Since you need to use a wildcard for the origin, you cannot set credentials: true 
  • In your client-side request, you can try setting headers: { "Content-Type": "text/plain" }

Also, here's a Node server-side example:

 

async function process_request(req, res) {

    res.setHeader('Access-Control-Allow-Origin', '*');
    let method = req.method.toUpperCase();

    if (method === 'OPTIONS') {
        res.setHeader('Access-Control-Allow-Methods', 'POST,GET');
        res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Cache-Control');
        res.setHeader('Access-Control-Max-Age', '3600');
        return res.status(204).send('');
    }
// the logic of the function
}

 

Hope that helps!

View solution in original post

Replies 8 (8)

kbarber
Shopify Partner
2 0 2

Following

ecbLR
Excursionist
11 0 2

I suspect our team is likely to run across this as we migrate our checkout.liquid pixels to extensibility. Also following. 

Bob-9x
Shopify Partner
1 0 0

Hi. I also posted the same problem. Can only be called for 3rd party url. It doesn't work for url stores. Is there any way to resolve it?

ecbLR
Excursionist
11 0 2

@Bob-9x do you have a link to your post? Would like to subscribe.

 

ecbLR
Excursionist
11 0 2

Bumping this as we have indeed run into this issue.

layerfive
Shopify Partner
3 0 3

We are running into this issue as well. @Shopify could you please respond to this?

Sizelabs
Shopify Partner
3 0 1

Same problem here, anyone got a solution for this?

mthdvd
Shopify Partner
26 1 10

This is an accepted solution.

Okay, so here's the situation.

 

The iframe that loads in the Customer Event strips the request of it's origin. Specifically, the origin appears to always be set to "null."

It seems this is just the way the iframe is set up to work by Shopify -- even when 'credentials: include' or 'mode: cors' is explicitly set on the request. Why and for what purpose, I cannot say. Why Shopify omitted mention of this I also cannot say, as it does impact how you need to set up your CORS configuration within your app.

 

In my case, I'm running Rails using rack-cors. To get things to work, a wild card must be used for the endpoint.

 

For example if my endpoint is: https://mysecureendpoint.com/product_views, CORS must be configured like this:

 allow do 
  origins '*'
  resource '/product_views', headers: :any, methods: [:post, :options]
end


My issue is that I was trying to configure things like this:

 allow do 
  origins 'https://storeurl.com'
  resource '/product_views', headers: :any, methods: [:post, :options]
 end

 

The issue with the above configuration is that it explicitly sets an origin. Since the origin will always be null coming from the iframe, an error will be thrown.

 

For the sake of completion here are some other things to consider or try if you're running into problems.

 

  • Since you need to use a wildcard for the origin, you cannot set credentials: true 
  • In your client-side request, you can try setting headers: { "Content-Type": "text/plain" }

Also, here's a Node server-side example:

 

async function process_request(req, res) {

    res.setHeader('Access-Control-Allow-Origin', '*');
    let method = req.method.toUpperCase();

    if (method === 'OPTIONS') {
        res.setHeader('Access-Control-Allow-Methods', 'POST,GET');
        res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Cache-Control');
        res.setHeader('Access-Control-Max-Age', '3600');
        return res.status(204).send('');
    }
// the logic of the function
}

 

Hope that helps!