Every visitor sees the same consent banner and the same tags fire regardless of jurisdiction — a Frankfurt user gets an opt-out notice that CCPA permits but GDPR forbids, and a California user is blocked from a sale-of-data toggle they are entitled to. The single point of failure is almost always a missing or misordered OneTrust Geolocation Rule that never maps the resolved region to a published consent template.

Triage: Confirm the Same Template Renders in Every Region

Before touching the OneTrust admin console, prove that the banner is region-blind and identify which template is being served everywhere.

  1. Open Chrome DevTools → ApplicationLocal Storage and delete every OptanonConsent and OptanonAlertBoxClosed key so the CMP re-evaluates from scratch.
  2. In the Console, read the geolocation OneTrust resolved for this session:
// OneTrust writes the resolved location into a global after the banner loads.
// OptanonGeolocation is set by the geolocation script (geolocation.json call).
console.log('Resolved region:', window.OptanonGeolocation);
// The active ruleset / template id is exposed on the OneTrust object:
console.log('Active group data:', window.OneTrust.GetDomainData().Groups.map(g => ({
  id: g.OptanonGroupId,       // C0001, C0002, C0004 ...
  name: g.GroupName,
  status: g.Status            // "active" / "always active" per template
})));
  1. Switch your egress region with a VPN (or override — see the Resolution section) to an EU IP, reload, and re-run the snippet. If window.OptanonGeolocation changes but the Groups array and their Status values do not, the geolocation rule is resolving the country yet no rule maps that country to a different template.
  2. Open the Network tab, filter for your vendor domains (google-analytics.com, connect.facebook.net, doubleclick.net) and confirm the same requests fire on both the EU and US loads. Identical requests across jurisdictions is the reproducible symptom.

Reproduction checklist:

OneTrust resolves a visitor’s country and state from their IP on every page load and exposes it as window.OptanonGeolocation. But resolving a location does nothing on its own. A Geolocation Rule is the object that binds a set of locations to a specific published consent template (banner + preference centre + default group statuses). Without a rule that names your regions, OneTrust falls through to the domain’s default template, so every visitor — Berlin, Los Angeles, or São Paulo — receives whatever behaviour that one template encodes.

The routing failure is three missing links in a chain:

  • No location grouping. The EU/EEA countries, US privacy-law states, and everywhere-else are not collected into distinct Geolocation Rule groups, so there is nothing to branch on.
  • No template binding. Even where a location group exists, it is not pointed at a template whose group defaults encode the correct legal basis — opt-in (default inactive) for GDPR versus opt-out (default active) for CCPA.
  • No routing read. The application injects tags from a static list rather than reading the resolved OneTrust groups, so the CMP’s decision never reaches the tag loader.

This is the concrete, vendor-specific implementation of the model described in the regional routing for CCPA and global privacy laws guide, and it depends on the jurisdiction-to-rule translation covered in mapping regional privacy laws to script routing rules. The legal basis determines the default group status the template ships; the geolocation rule determines which template a visitor gets; and the routing read turns the resolved groups into actual <script> injections.

OneTrust geolocation rule routing A resolved country enters an ordered rule stack evaluated top to bottom: rule 1 EU/EEA to the GDPR opt-in template with categories default off; rule 2 US privacy states to the CCPA opt-out template with categories default on; the default rule sends everywhere else to a no-banner template. Each template feeds a script router that injects the permitted vendor tags. Resolved country OptanonGeolocation Rule 1 (top) EU / EEA + UK Rule 2 US privacy states Default rule everywhere else GDPR opt-in template groups default: inactive CCPA opt-out template groups default: active No-banner template all groups active Script router inject per group

Resolution: Build Ordered Geolocation Rules and Read Resolved Groups

The fix has two halves: the OneTrust admin configuration that produces distinct resolved groups per region, and the client-side router that turns those groups into script injections.

Step 1 — Create location groups and publish three templates

In the OneTrust admin console under Geolocation Rules, create a rule group per legal regime rather than per country. Each rule binds a location set to a published template:

  • Rule 1 — EU/EEA + UK → GDPR opt-in template. Location set: all EEA member states plus the UK, Switzerland. Template: a banner with categories C0002 (Performance), C0003 (Functional), C0004 (Targeting) shipped inactive by default, requiring an explicit accept.
  • Rule 2 — US privacy states → CCPA/CPRA opt-out template. Location set: California, Colorado, Connecticut, Virginia, Utah, plus the newer state laws. Template: the same categories shipped active by default, with a “Do Not Sell or Share My Personal Information” link that flips C0004.
  • Default rule → no-banner template. Everywhere with no applicable law: all groups active, no banner (or a lightweight notice).

Order matters. OneTrust evaluates Geolocation Rules top-down and stops at the first match, so the most specific location sets must sit above broader ones. If a broad “North America” rule sits above the “US privacy states” rule, California resolves to North America first and never reaches the opt-out template. Drag the state-level rule above any country- or continent-level rule.

Step 2 — Bind the loader to data-domain-script

The single OneTrust CDN tag is what pulls the resolved template. The data-domain-script attribute is the domain UUID; append -test in staging to exercise draft templates without publishing:

<!-- Load OneTrust before any consent-gated vendor tag.
     The domain UUID resolves to the geolocation-selected template at runtime. -->
<script
  src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js"
  type="text/javascript"
  charset="UTF-8"
  data-domain-script="01920000-0000-0000-0000-000000000000"></script>

<!-- OptanonWrapper runs after the SDK has resolved geolocation + template -->
<script type="text/javascript">
function OptanonWrapper() {
  // Fires on load AND on every consent change — the routing hook.
  window.dispatchEvent(new CustomEvent('ot-consent-resolved'));
}
</script>

Step 3 — Read the resolved groups and route scripts

The routing read is the piece that most implementations skip. Instead of hard-coding which tags load, ask OneTrust which groups the resolved template activated, then inject only those vendors. OnetrustActiveGroups is a comma-delimited string of the currently-consented group IDs; OneTrust.IsAlertBoxClosed() confirms the banner has been resolved.

// A declarative map of vendor tags to the OneTrust category that gates them.
const SCRIPT_ROUTES = [
  { group: 'C0002', src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXX' }, // Performance
  { group: 'C0004', src: 'https://connect.facebook.net/en_US/fbevents.js' },     // Targeting
];

function routeScriptsForRegion() {
  // OnetrustActiveGroups looks like ",C0001,C0002,C0004," — pad-delimited.
  const active = window.OnetrustActiveGroups || '';
  for (const route of SCRIPT_ROUTES) {
    const permitted = active.includes(`,${route.group},`);
    const already = document.querySelector(`script[data-ot-src="${route.src}"]`);
    if (permitted && !already) {
      const s = document.createElement('script');
      s.src = route.src;
      s.async = true;
      s.dataset.otSrc = route.src;         // idempotency marker
      s.dataset.otGroup = route.group;     // for teardown on revocation
      document.head.appendChild(s);
    }
  }
}

// Run on initial resolution and on every subsequent consent change.
window.addEventListener('ot-consent-resolved', routeScriptsForRegion);

Because OptanonWrapper() re-fires on every consent change, a Berlin visitor who accepts targeting later triggers the same router and injects the Facebook tag on the spot — while that same tag ships already-active for a California visitor until they use the opt-out link. One code path, three jurisdictional outcomes, all driven by the resolved groups rather than the visitor’s raw country. For the teardown side of a later opt-out, hand the data-ot-group marker to the consent-syncing teardown across vendors logic.

To exercise a region without a VPN during development, override the resolved geolocation before the SDK stub loads:

// DEV ONLY — force the SDK to resolve as if the visitor were in California.
// Remove before production; overriding geolocation in prod is a compliance risk.
window.OneTrust = window.OneTrust || {};
window.OptanonGeolocation = 'US;CA';

Verification

Load the page from two egress regions (or with the dev override above) and diff the routing outcome:

// Run in Console on each region load, then compare the two JSON blobs.
JSON.stringify({
  region: window.OptanonGeolocation,
  activeGroups: window.OnetrustActiveGroups,
  injected: [...document.querySelectorAll('script[data-ot-src]')]
             .map(s => s.dataset.otGroup)
});

A correct configuration returns different activeGroups and injected arrays across regions: the EU load shows C0001 only until consent (no C0004 script node), while the US load shows C0001,C0002,C0004 with the Facebook tag already injected. Confirm in the Network tab that connect.facebook.net fires on the US load and is absent on the pre-consent EU load. That divergence is the single observable proof the geolocation rule now routes scripts per jurisdiction.

Common Pitfalls

  • Rule order inverted. A continent- or country-level rule placed above a state-level rule swallows the match, so California resolves to a generic US or North America template and never reaches the CCPA opt-out behaviour. Always order most-specific-first; OneTrust stops at the first hit.
  • Routing from raw country instead of resolved groups. Reading OptanonGeolocation and branching in your own code duplicates the template logic and drifts out of sync the moment a compliance owner edits a template. Route from OnetrustActiveGroups, which is the CMP’s authoritative decision.
  • Ignoring the re-fire of OptanonWrapper(). Treating it as a one-time load hook means a visitor who changes consent gets no new tags injected (or stale tags left running). It fires on every change — make the router idempotent with a data-ot-src marker so repeat calls neither double-inject nor miss late grants.

Up: Regional Routing for CCPA and Global Privacy Laws