Notes from Google Adwords Workshop at PubCon Florida 2019

In May of this year, I attended my first Pubcon. Specifically, I attended the Adwords workshop at Pubcon. Below are my thoughts from attending this event. Sorry, if they are a bit random, but its the best I could do while trying to collect and store all the instructions.

Ad Testing Ideas

Want to know the best ad for a targeted audience? Use Single-Ad Group Testing.

Segment your Ads: Brand vs Awareness.

Learn how to use Pivot Tables. (youtube videos on Pivot tables). It is an essential skill.

CTR is not a good metric for Ad Testing. It ignores the Bounce Rate entirely.

Favorite Metrics : Conversion Per Impression (CPI) or Revenue Per Impression (RPI).

Use Ad Extensions.

Keyword Changes

Semantic vs Syntactic Match Type

Phrase Match is a syntactic match type

Exact Match is a semantic match type.

Download your query data before 9/6 and 1/1. Do a Pivot Table, Match Type. Compare the exact match: did it improve or same or get worse?

Broad Match Type is a Semantic Match Type. Suggestion is to use a Broad Match Modifier like “+word” so the ad triggers only when “word” is contained in the search query.

Negative Keyword

Negative keywords do not match plurals. If the negative keyword is in anywhere in the search term, ad will not show.

Phrase Match Type vs Broad Match Type vs Exact Match for Negative Keywords.

Use Filters extensively

Cost $25.00; Coversions < 1, Added/Excluded – None – Terms that are not-converting and need to be removed.

CPA < Target CPA, Conversions > 3, Added/Excluded = None – > Make these keywords.

Tools To Use

  • Adwords Scripts – This is one of the most useful takeaways from the workshop. Since I attended the workshop, I have worked on improving my scripting skills. This has immense value at cutting down the manual effort and time needed for day-to-day maintenance.
  • Excel (Pivot Tables help you visualize the data and turn it into insight)
  • Adword N-Gram Analysis Tool (this is something new that I need to learn)

Expand Query Ads

Dynamic Search Ads – You do not write headlines, you tell Google to generate the headline based on the content of your website. Basically, match search keywords to landing pages automatically. DSA’s are useful for finding new search terms. It is dependent on good organic schema markup. Website structure will be an important factor for these DSA ads. You can also make negative dynamic targets.

Then, look at the Search Terms that are converting with DSA’s.

What’s New in Google Ads

  • Average Position in deprecated. It will disappear in September.
  • New Top IS Metrics: Abs Top Achieves, Top IS Achieved, Top IS Missed & Abs Top Missed. Conversion Rate is generally steady independent of position. CTR is higher for higher positions.
  • Advertisers should use conversion based bidding. For brand terms, you want to be in absolute top position.

Closed Variants

Closed variants work across all Match Types (Broad, Modified Broad, Exact Match, Phrase Match.

Responsive Search Ads

You should use a lot of variations. Mix the length of the headlines. RSA Ads: You give google headlines and descriptions, google will create ads.

Segment your analysis. Use Fred’s script to get the data. If the top performing ads are different for computer vs mobile vs tablet, then you should split the ads across different devices.

Use Landing Page screen to see which pages take longer to load. Work on optimizing page load time to increase conversions. Time To Load > 3 seconds is considered slow.

Uee Promotion Extensions to distinguish your ad from the competition.

Look at Giant Carousel Ads.

Look at ConversionLagBucket Added to Reports. Ignore the last 5 days of data because the conversion numbers have not been processed, if the average conversion lag is 5 to 7 days.

Google Shopping Campaigns

  • Use of smart shopping campaign is encouraged.
  • Item ID is used for correlating Quality Score. Try changing the product ID for improving CTR.
  • Recommended to place each product in its own product group. (think of it as Ad Group for non-ecommerce applications).
  • Single Product Ad Groups
  • SPAG – Each ad group has exactly one product group.
  • You can split the products by Product Category or Brand.

How to selectively enable/disable plugins on a per page/post basis using Plugin Organizer

I am in perpetual pursuite of reducing page time. I think it not only helps your SEO ranking but also provides a better user experience. So it is a win-win. That said, when I ran a Speed Test on one of the page, I noticed it was loading some external 3rd party scripts that were slowing down the TTFB and First Paint time.

Upon further digging using and, it was clear that Contact Form 7 (with Google Captcha enabled) was adding a few external scripts that were not needed. For example, I did not need the Contact Form 7 plugin loaded on the Cart and Checkout page.

What You Need


Without further ado, I will present below the steps I followed. But before we do that, let me first acknowledge that these are the steps provided to me by the author of the Plugin Organizer plugin. I do not take credit for these. The plugin author is very helpful and guided me through this process. I hope you will find it useful.

Step 1: Determine what scripts/styles need to be excluded

First, you have to determine what scripts/styles are slowing down your page. For this you can use or any other speed test site if your preference. Usually, I use both and together because they complement each other quite well.

Here is an example of the summary. Pay attention to the numbers highlighted. The page size and number of requests is very important for determining whether your un-needed scripts/styles are loading.

Fully Loaded Time, Total Page Size and Requests are important metrics that we will monitor

Now, let us dig a bit deeper. The Waterfall view of is where I find the scripts that I need to unload. The screenshot shows below the JS and CSS resources loaded for the page, sorted by Size. I find the Sorting feature useful because I can attain significant optimization by removing the bigger scripts/styles first.

All the CSS and JS resources loaded by the page (sorted by size)

After the speed analysis, I determined that I did not want Contact Form 7 to be loaded on all the pages.

Step 2: Configuration of Plugin Organizer

Brace yourself, this is not as easy as it seems. Plugin Organizer is a very powerful tool. It allows incredibly fine control over how and which plugins can be enabled/disabled on a certain page/post.

  • Install Plugin Organizer
  • Activate Plugin Organizer
  • Setup Plugin Organizer

Here is the screenshot for my settings for Plugin Organizer. Of note is the highlighted post types that I wanted to use Plugin Organizer for. Also, note the global settings. You can read up on each of these settings on the Plugin Organizer documentation site.

For me, the “Fuzzy URL Matching” and “Ignore URL Arguments” parameters need to be ON.

Disable the affected plugin globally. Here is the screenshot for my case – Contact Form 7 has been disabled Globally, i.e. it will not load at all on any pages/posts.

Step 3: Setup Plugin Filters

Here is the crux of it all. The logic, so to speak. In my case, I am enabling the CF 7 plugin on the 2 pages. Note the priority of filters.

Filter 1: CF 7

The 2 URL’s (permalinks) are the ones that this filter affects.

Filter 2: CF 7 JSON

In this case, there is only 1 URL that will be impacted. It is a specially formulated URL to select the URL that CF7 submits the form to. Notice the priority set to 4 in this case. The priority is important for this to work correctly, as it controls the execution order of the filters.

Step 4: Clear your cache(s)

After you have made the above changes, make sure to clear your cache(s) completely. Run the speed test again and verify that the un-needed scripts/styles are not being loaded anymore.


There you have it! With this configuration, I can successfully exclude a plugin from loading on a certain page/post. You may have to tinker with the priorities and Permalinks to get it working for your specific use case. Let me know in the comments if you think this is useful. For me, this was a great tool in easily reducing the page load time across the site in one shot. It is a clean way to exclude plugins from loading on every page.


[SOLVED] WordPress/WooCommerce Reset Password form not working

I am not sure when this started occurring, but recently a few of my users complained that they are unable to change their passwords. They click on the "Lost Password" link. They receive the email with the link to reset the password.

Upon clicking on the link, they are redirected to "https://…./my-account/lost-password/?show-reset-form=true". However, the Reset Password form does not show up. Instead, they receive the original Lost Password screen prompting them to enter username/email so the Lost Password email can be sent to them. 

So the users are in this circular loop. They are unable to change the password.

Looking at the relevant code (, it seems that the "wp-resetpass-" cookie is not being set. 

Ask your hosting provider if somehow they are caching the "my-account" and "lost-password" pages. If they are, that can explain this behaviour. Atleast, that was the case with me. Once those URL's were excluded from cache, the Reset Password" form showed up successfully. 

Have you experienced this issue before? What did you find?

ecommerce WooCommerce Wordpress

How to move the Archive Text in Genesis

For a while, I have struggled with how to remove or change location of the Archive Intro Text in a Genesis-based theme. Essentially, the idea is to move the Archive Intro Text towards the bottom of the page mostly for SEO purposes.

When I asked the questions on the StudioPress forums, I did not receive a satisfactory answer. So I did what most coders do: hack it till you discover how to do this way. Worry not, because this is not a hack. It is actually the recommended way to make a change like this.

Here is the code

/* Remove the Archive Headline and Text from the default location */
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_open', 5);
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_close', 15);
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_headline', 10);
remove_action( 'genesis_archive_title_descriptions', 'genesis_do_archive_headings_intro_text', 12);
/* Now, add it back at the bottom */
add_action( 'woocommerce_after_shop_loop', 'ar_add_custom_hook_archive_description', 8);
add_action( 'ar_add_wc_archive_descr_text' , 'ar_do_custom_archive_description_text', 7, 3);
function ar_add_custom_hook_archive_description() {
global $wp_query;
if ( ! is_category() && ! is_tag() && ! is_tax() ) {
$term = is_tax() ? get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) ) : $wp_query->get_queried_object();
if ( ! $term ) {
$heading = get_term_meta( $term->term_id, 'headline', true );
if ( empty( $heading ) && genesis_a11y( 'headings' ) ) {
$heading = $term->name;
$intro_text = get_term_meta( $term->term_id, 'intro_text', true );
$intro_text = apply_filters( 'genesis_term_intro_text_output', $intro_text ? $intro_text : '' );
do_action( 'ar_add_wc_archive_descr_text', $heading, $intro_text, 'taxonomy-archive-description' );
function ar_do_custom_archive_description_text( $heading = '', $intro_text = '', $context = '' ) {
if ( $heading || $intro_text ) {
genesis_markup( array(
'open' => '<div %s>',
'context' => $context,
) );
if ( $context && $intro_text ) {
echo $intro_text;
if ( $heading || $intro_text ) {
genesis_markup( array(
'close' => '</div>',
'context' => $context,
) );

view raw
hosted with ❤ by GitHub

The first 4 lines simply unhook the actions responsible for showing the Archive Intro Text at its default position. This will make sure the Archive Intro Text does not show up in its usual location.

The rest of the code moves the Archive Intro Text down to the bottom of the page, by hooking on to the “woocommerce_after_shop_loop” action. The trick is to get the relevant parameters passed to the function that eventually displays the Archive Intro Text.

This example is limited only to moving the Archive Intro Text. You will have to extend it to display the Archive Headline to the bottom.

Do let me know if you have any questions!

code ecommerce google analytics WooCommerce

How to Report WooCommerce Checkout Errors as Events in Google Analytics

For a while now, I have wanted to have visibility into what really happens on the WooCommerce Checkout page. More specifically, the goal is to determine why almost 50% of those visitors leave the site from the Checkout page?

Survey pop-ups could be one way of determining why they are leaving the site. However, personally, I consider those intrusive. Why not have a behind-the-scenes way of reporting the exact error(s) the user might be encountering?

At first, I envisioned that I could insert some JS code (specifically the GA Send Event JS code) within the relevant WooCommerce filter. In this case, it was going to be the “woocommerce_add_error” filter. However, that was not going to work, as I found that out after a bit of experimentation. Somehow, the JS code was being appended to the “checkout.min.js” file instead of being added inline. Later, I discovered that you can not add JS code in Filter Hooks. See this Facebook post in the Advanced WordPress group for that discussion.

The Solution

As I looked for how to solve this issue, another alternative method came to mind. What if I could trigger on the addition of the “<ul class=”woocommerce-error”>….</ul>” element to the DOM?

This way, the solution would be generic and can be extended for other notice types (warning, notice, message etc) as well. It could easily be used outside of the WooCommerce context as well. Ofcourse, you will have to change the correct class/id of the element being targeted.

Code Changes

function load_ga_error_script() {
// Load the JS file (jquery is a dependency)
wp_enqueue_script( 'ga-send-error-event', get_stylesheet_directory_uri() . '/js/ga-send-error-event.js', array('jquery' ) );
add_action( 'wp_enqueue_scripts', 'load_ga_error_script' );

view raw
hosted with ❤ by GitHub

jQuery(function() {
jQuery('.woocommerce').on('DOMNodeInserted', function(e) {
if (jQuery('.woocommerce-error')) {
// How Many Errors
var error_count = jQuery('.woocommerce-error li').size();
// This extracts the contents of the <li></li> tags, separated by commas
var messages = jQuery(".woocommerce-error li").map(function() { return jQuery(this).text() }).get().join();
// Send the GA Event
// Event Category: WooCommerce Error Event Action: checkout-error-notice Event Label: Actual Error Message shown to user
// Event Value: How many errors were shown to this user
ga("send", "event", "WooCommerce Error","checkout-error-notice", messages, error_count, {"nonInteraction": 1});


The code changes are split into 2 parts:

  1. Load the new JS file from within the functions.php of your child theme.
  2. ga-send-error-event.js should be located in the “js” folder of your child theme. If the “js” folder” does not exist, you will have to create it.


Once you make these changes, you can test it yourself in Google Analytics. But first, you will have to conduct your testing in an Incognito window (Google Chrome).

  1. Visit your Checkout page (preferably in an uncached and incognito browser session)
  2. Open your Developer Toolbar from the Browser (Chrome Dev Tools or FireBug)
  3. Leave all fields empty.
  4. Select a Payment Method and click on Continue
  5. You should see the messages printed above the Checkout Form
  6. Log into your GA interface
  7. Navigate to Real-Time->Events->Events (last 30 minutes)
  8. You should see new events generated under the “WooCommerce Error” category.

See screenshots below

New Category of events called WooCommerce Error
New Category of events called WooCommerce Error

In my case I had left the Billing First Name and Billing Last Name empty. Additionally, I had also left the “Terms & Conditions” checkbox unchecked. The Event Label got truncated, but if you hover over it, the full label is :

“Billing First Name is a required field.,Billing Last Name is a required field.,You must accept our Terms &amp; Conditions.”

Event showing Full error message
Event showing Full error message

So this is how I solved the issue of reporting error messages on WooCommerce checkout page. I am not sure how helpful this will be (I am hoping it will lead to better insight into the actions of the user on the Checkout page). Hopefully, it will reveal weaknesses in the UX.

What do you think? Is this something useful for you? Go ahead and use the code. Let me know if it works well for you. You can also easily extend this code to any part of the site (it does not have to be WooCommerce related).