partytown icon indicating copy to clipboard operation
partytown copied to clipboard

Google ads dont work

Open sushil-mirashi opened this issue 1 year ago • 15 comments

Google ads not working if loaded through partytown but other plugin work. Why?

sushil-mirashi avatar Feb 09 '23 13:02 sushil-mirashi

I want to know how i can add Google Adsense script in my project with partytown.

Do u also know how can I remove partytown console.log error from my production?

Aliwahid17 avatar Feb 10 '23 04:02 Aliwahid17

You can select which scripts should not be loaded by partytown. See https://partytown.builder.io/configuration loadScriptsOnMainThread

SGudbrandsson avatar Feb 13 '23 10:02 SGudbrandsson

I can confirm that Google Ads does not work with partytown. If you add Google ads tracking through gtag of GTM, it has to load a script:

https://googleads.g.doubleclick.net/pagead/viewthroughconversion/* , which redirects it to https://www.google.com/pagead/1p-conversion/XYZ/ then finally to https://www.google.com/pagead/1p-user-list/XYZ/

These redirects load every needed tracking cookie for it to work, however, since only the first URL can be resolved, it can not go through the chain. Even if I do a reverse proxy through CloudFront on the first googleads.g.doubleclick.net url, it will give CORS error on the second one.

I can not use loadScriptsOnMainThread either, because the script gets loaded by gtag/GTM.

I am not sure this is even possible to solve with partytown as it is now.

vptill avatar Mar 28 '23 16:03 vptill

@vptill doesn't loadScriptsOnMainThread handles any script loaded by partytown? While Adsense gets loaded by GTM, it is still being appended via script tag and loaded by partytown in the end. The problem is that the url search params appears to be variable, and can't be added to the loadScriptsOnMainThread list ahead of time, as partytown assumes strict equality of urls. But you could solve it by disabling loading Adsens via GTM and load it manually instead

andreyvolokitin avatar Apr 15 '23 21:04 andreyvolokitin

@andreyvolokitin I use gtag with Google Analytics and Ads tracking. That means, once I load GA, it will load the necessary scripts for tracking Ads as well, because the two are connected in GA4 and that is the recommended usage to track conversions in Google Ads. Separating the two is not really an option for me, the only solution would be to have reverse proxy on all the urls (https://googleads.g.doubleclick.net, google.com), not only the first one.

vptill avatar Apr 19 '23 18:04 vptill

Tried both using ads through GTM and integrating directly and neither worked. Sad to see that google ads can't work with partytown.

jeffreytung7 avatar Sep 07 '23 03:09 jeffreytung7

You can use server side tracking for that. We're going to utilize that.

Saves bandwidth and works with partytown

SGudbrandsson avatar Sep 07 '23 08:09 SGudbrandsson

Creating a separate layout for your conversion tracking page, which loads the scripts natively and using partytown on the other layouts, works as well.

vptill avatar Sep 07 '23 09:09 vptill

I got google ads loaded via GTM to work using cloudfront proxy to googleads.g.doubleclick.net. Screen Shot 2023-09-07 at 5 15 59 PM

I believe the "AllViewerExceptHostHeader" configuration was needed, else as @vptill says, google.com will throw the CORS error

ml27299 avatar Sep 07 '23 22:09 ml27299

A solution for this would be allowing loadScriptsOnMainThread to accept a function or regular expressions. This way, checking for dynamic URLs would be possible. I created a function that patches the files copied by copyLibFiles to make loadScriptsOnMainThread accept functions: https://gist.github.com/stldo/74a382ec9044315197cbcd0cf4832b1d. It seems to be working fine on Gatsby — at least no errors are displayed in the console —, but I don't have access to Google Analytics right now to confirm if the events are really coming.

Just call it after copyLibFiles:

import { copyLibFiles } from "@builder.io/partytown/utils";
import { enableLoadScriptsOnMainThreadCallback } from "./partytown";

export async function onPreBootstrap() {
  const basePath = path.resolve("static", "~partytown");
  await copyLibFiles(basePath);
  await enableLoadScriptsOnMainThreadCallback(basePath);
}

Then, in the Partytown component:

<Partytown
  key="partytown"
  forward={["dataLayer.push", "gtag"]}
  loadScriptsOnMainThread={(url: string) => {
    return url.startsWith(
      "https://googleads.g.doubleclick.net/pagead/viewthroughconversion",
    );
  }}
/>

stldo avatar Sep 08 '23 04:09 stldo

Is there any chance we could get a PR with at least a breaking test? Once we have a breaking test this would be so much easier to fix for us...

mhevery avatar Sep 08 '23 17:09 mhevery

@mhevery I added the setup to tests/react-app, but cant create a PR since I don't have read access. I'm not very familiar on how to do this with a repo I dont own

ml27299 avatar Sep 08 '23 20:09 ml27299

its very simple setup, basically you have

GoogleAnalytics.tsx

import React from 'react';

const trackingScript = (GA_TRACKING_ID: string, SITE_NAME: string, GA_TAG_ID: string) => {
	return `
	window.GoogleAnalyticsObject = 'ga';
	window.ga = function () {
		window.ga.q = window.ga.q || [];
		window.ga.q.push(arguments);
	};
	window.ga.l = 1 * new Date();

	window.ga("create", "${GA_TRACKING_ID}", "${SITE_NAME}.com");
	${GA_TAG_ID ? `window.ga("require", "${GA_TAG_ID}")` : ""};
	window.ga("send", "pageview");
	window.ga("require", "ecommerce");`;
};

interface GoogleAnalyticsProps {
    GA_TRACKING_ID: string;
    SITE_NAME: string;
    GA_TAG_ID: string;
}

export const GoogleAnalytics: React.FC<GoogleAnalyticsProps> = (props) => {
  return (
    <>
        <script
            type="text/partytown"
            async
            defer
            src="https://www.google-analytics.com/analytics.js"
        />
        <script
            type='text/partytown'
            defer
            dangerouslySetInnerHTML={{
            __html: trackingScript(props.GA_TRACKING_ID, props.SITE_NAME, props.GA_TAG_ID),
            }}
        />
    </>
  );
};

GoogleTagManager.tsx

import React from 'react';

const trackingScript = (GA_TAG_ID: string) => {
	return `
	window.dataLayer = window.dataLayer || [];
window.gtag = function () {
	dataLayer.push(arguments);
};
window.gtag('js', new Date());
window.gtag('config', '${GA_TAG_ID}');
    `;
};

interface GoogleTagManagerProps {
    GA_TAG_ID: string;
}

export const GoogleTagManager: React.FC<GoogleTagManagerProps> = (props) => {
  return (
    <>
       <script
            type="text/partytown"
            async
            src={`https://www.googletagmanager.com/gtag/js?id=${props.GA_TAG_ID}`}
			/>
        <script
            type='text/partytown'
            defer
            dangerouslySetInnerHTML={{
            __html: trackingScript(props.GA_TAG_ID),
            }}
        />
    </>
  );
};

Add the 2 files to the head of the page with the correct ids GA_TAG_ID - google tag id GA_TRACKING_ID - google analytics id SITE_NAME - some site name

when you load this and look at the network calls you'll see that google tag manger calls "googleads.g.doubleclick.net" which then redirects to https://www.google.com/pagead/1p-conversion/XYZ/ and https://www.google.com/pagead/1p-user-list/XYZ/

Partytown's "resolveUrl" only captures up to googleads.g.doubleclick.net, so its not so trivial to create a reverse proxy for it because when it redirects to google.com you'll get a CORS issue. As I mentioned in my solution, all you need to do is add a request policy to the reverse proxy (that points to googleads.g.doubleclick.net) to forward all the parameters in viewer request, this allows CORS to work thru the whole chain of redirects

ml27299 avatar Sep 08 '23 21:09 ml27299

@mhevery I added the setup to tests/react-app, but cant create a PR since I don't have read access. I'm not very familiar on how to do this with a repo I dont own

  1. Fork the repo https://github.com/BuilderIO/qwik/fork
  2. Create a tests here: https://github.com/BuilderIO/partytown/tree/main/tests/integrations
  3. Send a PR please..

mhevery avatar Sep 11 '23 17:09 mhevery

I initialized google ads using partytown, and it is great! Did that with reverse proxying on every url (just wait for the cors error and do reverse proxy for that url, then catch another in console log and so on... there is about 10 urls). Also pay attention on some responses if your reverse proxy server is not in same country/state as your website is. I had a problem with one url which response was different when i requested it from reverse proxy server comparing to response requested from my website. I just created another one in my country and it worked!

The problem I saw is that it loads only few of possible ads. As i am using setupAd in combination with google ads I suspect that it only loads ads from setupAd or Google (not sure). I can only say I am getting this warning: Partytown unable to get cross-origin cookie: // gum.criteo url loads iframe and there in that iframe i can see: document.cookie And it is expected behavior as partytown says:

If an iframe was created with Partytown, and the iframe is cross-origin, then Partytown is unable to get or set the origin’s document.cookie, localStorage or sessionStorage. The code will continue to work, however, cookies and storage will not persist.

Setting document.cookie is a noop and will not assign anything, and getting a cookie will always return an empty string. Both localStorage and sessionStorage work as expected, however, the localStorage data is not saved across browser sessions. In these scenarios a console.warn will be logged.

Then i tried to load that iframe on main thread using loadScriptsOnMainThread but it doesn't work (because it works only with js url and id or it doesn't work for dynamic loaded scripts).

And that's where i got stuck. Does anyone have an idea how to "bypass" that behavior? Is there any chance i can forward only event calling that url?

filiptunguz avatar Sep 12 '23 10:09 filiptunguz