1. A duplicate can hide a configuration problem
A developer installs WhoVisited in a shared application layout. Later, someone adds the same tracker through Google Tag Manager to manage marketing scripts centrally. Both snippets look reasonable on their own. Together, they create two places to change the site ID, consent setting and collection endpoint.
Do not assume every duplicate script element means every page view is counted twice. The current WhoVisited tracker checks window.whovisited before initialization and exits if that global already exists. Within a single window, a later copy normally does not create another tracker instance.
That guard prevents a second initialization, but it does not reconcile the configurations. The first copy that successfully initializes determines the site ID and initial consent mode. With asynchronous loading, visual order in the document is not a reliable way to choose a winner. An outdated copy can therefore explain missing activity under the website you expected, even when a newer snippet is present.
2. Inventory actual script elements on the deployed page
Start on your deployed HTTPS website after scripts have had time to load. Run this read-only diagnostic in its browser console. It lists elements whose script URL has the tracker.js path, including copies with cache-busting query parameters.
function findWhoVisitedScripts() {
return Array.from(document.scripts).flatMap((script, index) => {
if (!script.src) return [];
const url = new URL(script.src, location.href);
if (url.pathname !== "/tracker.js") return [];
return [{
documentIndex: index,
scriptOrigin: url.origin,
siteId: script.getAttribute("data-site-id"),
consentMode: script.getAttribute("data-consent") || "default",
apiOverride: script.getAttribute("data-api-url") || "none",
}];
});
}
console.table(findWhoVisitedScripts());One intended tracker element is the target setup. If the result shows two copies with different site IDs or consent modes, treat that as a configuration conflict to resolve. Compare IDs privately with the matching website card in Settings. Do not publish screenshots with private endpoint details.
The DOM list is an inventory, not an execution log. A script loader can remove an element after execution, and a custom proxy can use another file path. Also inspect the Network panel for tracker.js, bootstrap and events. The request initiator can help you trace a load back to a layout, a tag-manager container or another loader.
A 200 response for tracker.js proves file delivery, not event acceptance. A healthy tracking flow continues through bootstrap and the events POST. Keep the troubleshooting reference nearby if either stage fails.
3. Choose one installation owner
Pick the application code or the tag-manager container as the owner of tracker loading for a given page. Record the file or tag name, intended website ID, supported page scope and consent integration. The useful rule is a single maintained installation, not a preference for one tool.
For a Next.js App Router application, use next/script in the layout covering the routes you want to measure. Next.js documents that a layout script is loaded once across navigation within that layout. A root layout is appropriate when the intended scope is the whole application. Do not additionally paste the tracker into every page component or a second container tag.
For Google Tag Manager, the Custom HTML tag is the place for a snippet that lacks a dedicated template. Paste the complete installation snippet from WhoVisited Settings. Inspect the triggers and consent conditions for that tag. Preview it with Tag Assistant before publishing so you can see when it fires and what triggered it.
Tag-manager preview checks the container configuration; it does not prove a tracker was accepted by WhoVisited. Verify the collection requests and website activity as well. Publishing a container and deploying application code are separate operations, so note which version is live before comparing the result.
5. Migrate with an explicit cutover
Suppose you are moving from a layout snippet to Google Tag Manager. Prepare the replacement tag with the same website ID and intended consent behavior, but keep its live activation controlled until you can retire the old entry point. Use a staging environment or tag-manager preview to check the replacement without treating that draft as the public deployment.
- Save the current application and container versions so you can restore the previous installation.
- Compare the old and proposed site ID, endpoint override, page scope and consent setting.
- Publish or deploy the removal and replacement in a coordinated change, minimizing an overlap or gap.
- Reload a new document and inspect the script inventory, bootstrap request and accepted events.
- Check pages that use different layouts or templates, not only the homepage.
Do not try to repair the current page by deleting window.whovisited in the console and loading another snippet. The first instance may still have timers and history listeners. Removing its global does not unwind those effects and is not a clean migration test. Reload after deploying the intended single installation.
Likewise, changing an already initialized script element’s data-site-id does not retarget that instance. The tracker reads its configuration at initialization. Fix the source of the snippet and start a fresh page load before judging the result.
6. Verify behavior with a short test matrix
- Fresh landing: one intended installation, the expected site ID in bootstrap, and a newly accepted page-view event.
- Client-side route change: the tracker remains available and the new path appears without adding another script.
- Browser Back: the expected path is recorded when navigation changes the pathname.
- Consent waiting: collection follows the configured waiting behavior, rather than an earlier duplicate enabling it.
- Consent approval: your consent integration reaches the initialized tracker after readiness.
- Business event: a confirmed action reaches the intended website and exact goal name.
Use synthetic test actions and note the time and timezone. An events POST can batch multiple events, so the number of POST requests is not a page-view counter. Inspect the event names and paths privately, then confirm the visitor journey under the correct website.
Repeat the check outside preview mode after the public container or application version is live. Cached pages and long-lived tabs can still be using an earlier configuration. A fresh document load gives you a clearer test than continuing an old tab after editing the installation.
7. Keep the conclusion narrower than the evidence
Two script elements do not establish double-counted visits, and one element does not prove every event was delivered. Duplicate business handlers, another browser context, a different analytics product or two real visits can produce patterns that look similar in a dashboard.
The WhoVisited initialization guard is scoped to a window, not a universal lock across tabs, frames or devices. If your site intentionally includes an embedded application, inspect its tracking context separately. Do not use conflicting snippets on one page as a way to route the same tracker to two website IDs.
Finish with a small integration record: who owns the installation, where it lives, which site it targets, and how you verified a new page view and a meaningful action. That record is easier to maintain than relying on whichever asynchronous copy happens to initialize first.