The Global Header Box Is a Dumping Ground: Building a Script Loader You Can Hand Over
Photo: Internet Archive Book Images, No restrictions, via Wikimedia Commons
Every UK agency has at least one of these in its portfolio: a site where the template is off-limits. WordPress with a page builder bolted on, a Shopify theme nobody wants to touch before Black Friday, or a hosted platform whose only opening is a “custom HTML” box in the global header. Someone once put a script tag in there. Then someone else added another. Now the header is a dumping ground and nobody knows which tag does what.
The usual advice is to rewrite the theme or install a tag manager. Both are bigger jobs than they sound, and neither solves the handover problem. What you actually need is a controlled loading layer: one small module that decides what loads, when, and why. No framework rewrite. No plugin roulette.
Take inventory before you touch anything
Open the site in a private window. In DevTools, go to the Network tab, filter by JS, and reload. Write down every script, its size, and whether it appears on every page. Then look at the page source and search for <script. You will usually find three categories: platform scripts you cannot move, plugin scripts that were enqueued badly, and hand-added tags in the header box.
The hand-added tags are your territory. The platform scripts are not. The plugin scripts are a negotiation. Knowing the difference stops you from breaking checkout while chasing a tidy console.
If the site is WordPress and you have admin access, install Query Monitor on a staging copy or a local clone. It shows which plugin enqueued which handle, and it is far quicker than reading PHP by torchlight. On Shopify or a hosted builder, you will be reading the rendered source and guessing. That is fine. The inventory is the point, not the tool.
Put one loader in the header box, not twelve tags
The header box should contain exactly one script tag: your loader. Everything else gets registered inside it. That gives you a single place to add conditions, catch errors, and document decisions.
Keep the registry boring. A plain array of objects is enough:
const registry = [
{
id: 'booking-widget',
src: '/assets/booking.js',
when: () => document.querySelector('[data-booking]')
},
{
id: 'store-locator',
src: '/assets/locator.js',
when: () => document.querySelector('[data-store-locator]')
}
];
Then loop over it, check the condition, and inject the script once. Use a data-loader-id attribute on the script element so you can see what loaded in the DOM. If a condition throws, catch it and log a warning. A broken condition should not take down the page.
The loader itself should be tiny. If it grows past a hundred lines, you are probably rebuilding a tag manager by accident. Resist that. The value here is clarity, not features.
Conditions that survive a handover
Use page structure and routes, not user-agent strings. A booking widget should load where the booking form is, not on the homepage because someone once saw it there. A store locator should load on the locator page and nowhere else. If the platform gives you a body class or a template name, use it.
For UK sites with analytics or marketing pixels, gate those behind your consent mechanism. The loader can check a cookie or a global consent object before injecting. That is not a legal opinion, but it is a practical way to stop tracking scripts firing before someone has made a choice.
Avoid loading on scroll unless the script is genuinely below the fold and heavy. The intersection observer pattern is fine, but it adds complexity. Most small sites do not need it. Load on page type, element presence, or a query parameter for previews.
The jQuery trap and other duplicate conflicts
Page builders are notorious for shipping jQuery. So are plugins. If you add another copy in the header box, you get two versions fighting over $, and the console fills with errors that look like someone else’s fault.
On WordPress, use wp_enqueue_script with the correct dependencies instead of hard-coding a script tag. If the platform already has jQuery, declare it as a dependency and let WordPress sort out the order. On Shopify or a hosted builder, check window.jQuery before loading your own. If it exists, use it. If the version is too old for your script, fix your script rather than forcing a second copy onto the page.
Name your script IDs with a prefix, such as ml-booking, and check for an existing element with the same ID before injecting. That one guard stops most duplicate-load bugs. Also decide on load order deliberately: dependencies first, then your module. Do not rely on the order of tags in the header box, because someone will reorder them during a panic fix.
Test without cloning the whole site
A full staging clone is ideal, but it is not always available. Shared hosting, locked platforms, and client IT departments all have a way of making that difficult. You can still test the loader safely.
Add a debug flag to the loader: if the URL contains ?loader=debug, log every registry decision to the console and add a visible badge to the page. Use a preview link or a password-protected page. Test one route at a time: homepage, a product page, a checkout page if you can. Throttle the network in DevTools to “Slow 3G” and watch whether the page still feels usable. Ask a colleague on a different connection to click through the main journey.
For WordPress, you can also use the theme file editor on a staging site to add the loader as a mu-plugin. That keeps it out of the page builder and makes it easier to version. If you cannot access the filesystem, the header box is still fine, but document that choice so the next person knows where to look.
Handover is a README, not a Slack thread
Write down the registry entries, what each one does, and the condition that loads it. Put that in the repo README or a wiki page, not in a Slack message that will be buried by lunchtime. Include a line about how to add a new script: add an entry, pick a condition, test with the debug flag, update the README.
That is the whole point of the loader. It turns a pile of anonymous tags into a list you can reason about. The next developer does not need to reverse-engineer the header box. They read the registry, see the conditions, and add their script without breaking yours.
It is not glamorous. It is a small piece of infrastructure that makes a locked-down page builder behave like something you control. In agency work, that is often the difference between a clean handover and a 6pm phone call.