Adding an assistant to a WordPress site

Three ways to add the embed, which one to choose, and the caching and consent issues specific to WordPress.

Vishal ChiniwarCo-founder and CTO2026-05-271,213 words

There are three common ways to add a widget script to WordPress, and the difference between them matters more than it appears, because two of them can silently lose your code.

Editing footer.php directly works until the theme updates, at which point your change is overwritten. If someone has done this and the widget disappeared after a theme update, this is why. Use a child theme if you must edit template files at all.

A small plugin whose only job is injecting scripts into the head or footer is the pragmatic choice for most sites. It survives theme updates, it is visible to whoever inherits the site, and it can be removed cleanly.

Option 3: enqueue properly in a child theme functions.php, best for developers

Using wp_enqueue_script with the correct hook is the technically correct approach. It participates in dependency ordering and can be conditionally loaded per template.

add_action('wp_enqueue_scripts', function () {
 wp_enqueue_script(
 'site-assistant',
 'https://example.com/widget.js',
 array(),
 null,
 true // load in footer
 );
});

The final true is the important argument. It puts the script in the footer rather than the head.

Caching will hide your change

This is the single most common WordPress support issue with any embed. Page caching plugins and CDN layers will serve the old HTML without your script for anywhere from minutes to hours. Before concluding the embed failed:

  • Purge the page cache plugin.
  • Purge the CDN cache if one is in front.
  • Test in a private window, since your logged in view often bypasses cache and shows a different result from what visitors see.

That third point causes real confusion, because the site owner sees the widget and the visitor does not, or the reverse.

Many WordPress sites run a consent management plugin that blocks third party scripts until consent is given. These plugins often categorise unknown scripts as marketing or analytics by default, which means the widget will not load for anyone who has not accepted that category.

This is not a bug, and it may well be the behaviour you want. But decide it deliberately: is a support widget functional or marketing under your own consent model? Then configure the plugin to match, rather than discovering by accident that the widget only loads for some visitors.

Training sources

WordPress sites usually expose a sitemap, either from the core or from an SEO plugin. Exclude tag archives, author archives, date archives and paginated listing pages. These are structural pages with little prose and they add noise without adding answers.

Where to put the snippet without a plugin

The theme footer is the obvious place and the wrong one, because a theme update overwrites it and nobody connects the disappearance to the update.

A child theme survives updates and is the correct answer if one already exists. Creating a child theme purely to hold one script tag is heavier than the problem deserves.

The lightest durable option is a small snippet plugin, or a single function hooked to `wp_footer` in a site specific plugin file. Both survive theme changes, and both keep the script somewhere a developer will look.

What WordPress makes hard for a crawler

Page builders are the main obstacle. Output varies enormously, and some builders wrap readable text in nested markup that extracts poorly, producing passages full of layout fragments rather than prose.

Shortcodes that render dynamic content are invisible if they run client side, and perfectly readable if they render server side. There is no way to tell from the editor which you have, so check the rendered source rather than assuming.

Membership and paywall plugins hide content from anonymous visitors, which means the crawler sees the teaser and not the article. Gated documentation has to be uploaded as files if you want it answerable.

Custom post types index normally and are frequently the cleanest source on a WordPress site, for the same reason CMS collections are on Webflow: they are structured.

Reindexing on a platform that publishes constantly

WordPress sites tend to publish more often than marketing sites on other platforms, which makes staleness a more active concern rather than a theoretical one.

The advantage is that WordPress can emit an event. A publish hook can trigger a reindex, which removes the gap entirely for the pages that matter. This is one of the few places where the platform makes freshness easier rather than harder.

The thing to avoid is reindexing on every save. Drafts and autosaves would trigger it constantly and index content that was never published. Hook the transition to published status, not the save.

Plugins that quietly break the assistant

Three categories cause most of the problems, and all three are things a site already has before the assistant arrives.

Aggressive caching plugins can serve a cached page without the newly added script for hours. The fix is a cache purge after adding the embed, and the symptom is an assistant that appears for you and not for anyone else.

Script optimisation plugins that defer, combine or lazy load JavaScript can delay or break the widget. Excluding the assistant script from optimisation is usually a one line setting and is worth doing before debugging anything else.

Consent management plugins may block the script until a category is accepted, which is often correct behaviour and occasionally means the assistant never loads because it was filed under the wrong category.

A note on multisite

On a multisite network, custom code added at the network level applies everywhere, which is almost never what you want, because each site has different content and needs its own agent.

Add the embed per site with a per site agent. The alternative, one agent trained across every site in the network, produces answers drawn from a sibling site, which is confusing at best and a disclosure problem where the sites belong to different clients.

Checking what actually got indexed

WordPress sites accumulate content in ways nobody fully remembers, and the gap between what you think is indexed and what is indexed is usually larger here than on other platforms.

Start with the sitemap your SEO plugin generates. It already reflects decisions about what is public and canonical, and it will typically include archive pages, author pages, tag pages and attachment pages that you do not want as sources.

Attachment pages are the classic problem. Many installations publish a page per uploaded image, containing almost no text, and indexing several hundred of them fills the index with passages that can never answer anything while competing for retrieval.

Category and tag archives are the second. They are lists of excerpts, so they retrieve well against general questions and answer none of them, which is the worst combination.

Exclude both, index posts and pages selectively, and check the resulting count against what you expected. A source count several times larger than your real page count is the signal that archives got swept in.

Questions

A snippet plugin is the safest of the three. Editing the theme means the embed is lost at the next theme update, and tag manager adds a dependency that also has to be loading for the widget to exist.

It can. Aggressive HTML minification sometimes mangles inline script tags. If the widget vanishes after enabling a cache plugin, exclude the snippet before assuming the embed is wrong.

Yes. The embed is a script tag and does not care what rendered the page. What matters is whether the builder outputs your content as readable HTML, which most do.

If your banner blocks scripts until consent, the widget is blocked with them. That is usually correct behaviour and worth deciding deliberately rather than discovering.

No, and you should not. Index what a customer could reach. Admin pages, staging drafts and internal notes produce confidently wrong answers about things visitors cannot see.

It should not. The script is deferred and does not block rendering. If your measured LCP moves, the widget is loading eagerly and that is a configuration problem.

Signal

Have a question about how this works?

Access is by waitlist, demo request or public-content pilot. Ask directly and we will answer, including where it will not fit.