Skip to content

CORS - Shopify Customer Events #278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
WebKenth opened this issue May 19, 2025 · 7 comments
Open

CORS - Shopify Customer Events #278

WebKenth opened this issue May 19, 2025 · 7 comments

Comments

@WebKenth
Copy link

Greetings!

I am attempting to create a Shopify Customer Event Pixel but i am running into CORS Issues and i don't really understand why there's CORS involved, perhaps we can get some clarifications surrounding how tracking is actually handled.

I'ved created a site without www. but my site had www. i can see #148 and #172 both mentions something about having www. or not
I'ved tried creating a new site without www. but same result as below:
Image

The script loads without issue but then the first event triggers and breaks due to CORS
Image
Image

If i use the script in the theme files there are no issues

    <script
      src="https://<host>/api/script.js"
      data-site-id="<site-id>"
      defer
    ></script>

Side note: It seems the script is attempting to push an event with the event name as blank, with a type of "page_view" even though data-track-spa is false? is this expected or a bug

Here's the full script if you want to debug it, though i breaks :

const script = document.createElement('script')
script.defer = true
script.src = 'https://<your-host>/api/script.js'
script.setAttribute('data-site-id', "<your-site>");
script.setAttribute('data-track-spa', "false");
script.onload = () => {
  /*
  Customer Event pixels are abit weird with page views, so disabled it for now
  analytics.subscribe('page_viewed', event => {
    window.rybbit.pageview();
  })
  */

  analytics.subscribe('cart_viewed', event => {
    window.rybbit.event('cart_viewed')
  })

  analytics.subscribe('product_viewed', event => {
    window.rybbit.event('product_viewed', {
      product_id: event.data.productVariant.id,
      handle:     event.data.productVariant.product.handle,
      name:       event.data.productVariant.product.title,
      price:      event.data.productVariant.price.amount,
      currency:   event.data.productVariant.price.currencyCode
    })
  })

  analytics.subscribe('collection_viewed', event => {
    window.rybbit.event('collection_viewed', {
      collection_id: event.data.collection.id,
      handle:        event.data.collection.handle,
      title:         event.data.collection.title
    })
  })

  analytics.subscribe('search_submitted', event => {
    window.rybbit.event('search_submitted', {
      query: event.searchResult.query
    })
  })

  analytics.subscribe('product_added_to_cart', event => {
    window.rybbit.event('product_added_to_cart', {
      variant_id: event.data.cartLine.merchandise.productVariant.id,
      quantity:   event.data.cartLine.quantity
    })
  })

  analytics.subscribe('product_removed_from_cart', event => {
    window.rybbit.event('product_removed_from_cart', {
      variant_id: event.data.cartLine.merchandise.productVariant.id
    })
  })

  analytics.subscribe('checkout_started', event => {
    window.rybbit.event('checkout_started', {
      checkout_id: event.data.checkout.id,
      item_count:  event.data.checkout.lineItems.length,
      subtotal:    event.data.checkout.subtotalPrice.amount,
      currency:    event.data.checkout.subtotalPrice.currencyCode
    })
  })

  analytics.subscribe('checkout_contact_info_submitted', event => {
    window.rybbit.event('checkout_contact_info_submitted', {
      checkout_id: event.data.checkout.id,
      email:       event.data.checkout.email
    })
  })

  analytics.subscribe('checkout_address_info_submitted', event => {
    window.rybbit.event('checkout_address_info_submitted', {
      checkout_id: event.data.checkout.id,
      shipping_address: event.data.checkout.shippingAddress
    })
  })

  analytics.subscribe('checkout_shipping_info_submitted', event => {
    window.rybbit.event('checkout_shipping_info_submitted', {
      checkout_id: event.data.checkout.id,
      shipping_rate: event.data.checkout.shippingRate
    })
  })

  analytics.subscribe('payment_info_submitted', event => {
    window.rybbit.event('payment_info_submitted', {
      checkout_id: event.data.checkout.id,
      payment_provider: event.data.checkout.paymentProvider
    })
  })

  analytics.subscribe('checkout_completed', event => {
    window.rybbit.event('checkout_completed', {
      order_id: event.data.checkout.id,
      value:    event.data.checkout.totalPrice.amount,
      currency: event.data.checkout.totalPrice.currencyCode,
      items:    event.data.checkout.lineItems.map(li => ({
        variant_id: li.merchandise.productVariant.id,
        name:       li.merchandise.productVariant.title,
        price:      li.merchandise.productVariant.price.amount,
        quantity:   li.quantity
      }))
    })
  })
}
document.head.appendChild(script)
@WebKenth
Copy link
Author

Setup Context: Using Docker on a hetzner instance

With my own nginx infront with the config:

    # API requests
    location /api/ {
        proxy_pass http://localhost:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
 
    # Client app
    location / {
        proxy_pass http://localhost:8081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

@WebKenth
Copy link
Author

It also seems to block localhost development which is kinda a bummer

Can't even create a site without it adhering to the domain.tld requirements :(
Image

Wonder if there would be a benefit to adding a CORS Setting on each site to allow users to disable it

I can easily imagine wanting to send events from other sites than the one you are monitoring

We have 2 shops which emit events to each other based on certain conditions, albeit mostly just for ease of looking at the analytics

@WebKenth
Copy link
Author

WebKenth commented May 19, 2025

Noticed the Cors server is setup in

server.register(cors, {
origin: (origin, callback) => {
if (!origin || allowList.includes(normalizeOrigin(origin))) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"), false);
}
},
credentials: true,
});

I realize it would hurt performance to do a check with each site every request, albeit could be mitigated with some caching

Though it would greatly improve the usability + setup of new sites without the requirement of a domain. In Theory the CORS part is the only thing thats causing these Domain requirements.
Since the tracking script has an ID attached to it, we don't really require anything else from the script to identify which site to send events to. So the CORS should ideally just be a setting you could enable incase you wanted to ensure your site domain can only receive traffic from that domain

Opening up the CORS would also allow for server side tracking for applications that want to use Rybbit for that type of analytics, like Apps

@ECuiDev
Copy link
Collaborator

ECuiDev commented May 20, 2025

I will look into this.

@davidfiala
Copy link

e0a8634

That specifically broke me.

If a site is registered as www.site.com in the UI, why would we trim it? That's what hit me. I had to rename my site in rybbit to site.com rather than www.site.com

I'd say that users that wish to have the "normalized" version could simply strip the www in the admin panel.

I explicitly prefer to have different sites, a.foo.com, b.foo.com, c.foo.com each of which has their own site registration.

@Venipa
Copy link

Venipa commented May 23, 2025

It also seems to block localhost development which is kinda a bummer

Can't even create a site without it adhering to the domain.tld requirements :( Image

Wonder if there would be a benefit to adding a CORS Setting on each site to allow users to disable it

I can easily imagine wanting to send events from other sites than the one you are monitoring

We have 2 shops which emit events to each other based on certain conditions, albeit mostly just for ease of looking at the analytics

@WebKenth why would you track ur local environment...

@davidfiala
Copy link

davidfiala commented May 26, 2025

@WebKenth 's proposal of having a per-site CORS list makes sense to me. It would allow us to make a site name purely a human readable label, and let a CORS list be the source of truth for which domains are meant to by tracked on a per-property basis.

Additionally, it may allow non-standard ports to also function with CORS, in case someone is doing https on a non-443 port, too.

There may be situations where an automation-driven browser integration test would need to be tracked on custom domains. Or when you use A/B domains for prod experiments. etc.

The problem right now is that the concept of a site is explicitly tied to a single domain, and we currently even break "www.site.com" by normalizing it to site.com.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants