Add Cookie Compliance to a plain HTML site
In short: publish the app, then paste the live two-tag snippet (from MCP install.getSnippet or the dashboard Integrations page) as the first thing inside <head> on every page, above every other script. Leave your analytics tags where they are, below it.
You need #
- A Cookie Compliance AppID, for example
examplecom-1a2b3c4. You'll find it in the Cookie Compliance dashboard. No account yet? Sign up; the free plan needs no card. - Access to edit the HTML of every page, or the shared header file they all include.
The wrong way #
<head>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<script>/* gtag config */</script>
<!-- banner added last, "so it doesn't slow the page down" -->
<script async src="https://cdn.hu-manity.co/hu-banner.min.js"></script>
</head>
The banner shows up, but Google Analytics has already run and set its cookies before the visitor sees it. The consent is decoration. Three things are wrong here:
- The snippet comes after a tracker. The widget can only block scripts that haven't run yet.
asynclets the browser run it whenever it likes, so the order is lost.- The
huOptionsblock is missing, so the widget doesn't know which app it belongs to.
The right way #
Get the live snippet — do not rebuild it from memory. Publish first, then copy: click Publish Now in the dashboard, then copy the snippet from Integrations → Manual Integration. If you copied it while the app was unpublished, copy it again after publishing. Or ask an agent that has the Cookie Compliance MCP server connected to call
install.getSnippet(also listed asinstall_getSnippet) with your AppID. Paste the returned HTML unchanged. Once the app is published, that source picks the script URL for your app's banner engine (to check it matches, see step 0 of verify-install):- v2 (a new app):
https://cdn.hu-manity.co/v2/hu-banner.min.js - v1:
https://cdn.hu-manity.co/hu-banner.min.js— the owner asked for v1, or this app was already on v1
Shape (keys and URL vary — treat this as illustration only):
<script> var huOptions = { "appID": "YOUR_APP_ID", "currentLanguage": "en", "blocking": true, "globalCookie": false }; </script> <script src="https://cdn.hu-manity.co/v2/hu-banner.min.js" type="text/javascript" charset="utf-8"></script>If you later switch banner engine in the dashboard, re-copy and replace the snippet on every page. Changing the setting alone does not update a hand-pasted install.
- v2 (a new app):
Put it first in
<head>, before anything else that runs code:<head> <meta charset="utf-8"> <!-- Cookie Compliance: FIRST script on the page (paste live snippet here) --> <script> var huOptions = { "appID": "YOUR_APP_ID", "currentLanguage": "en", "blocking": true, "globalCookie": false }; </script> <script src="https://cdn.hu-manity.co/v2/hu-banner.min.js" type="text/javascript" charset="utf-8"></script> <!-- your existing tags stay here, unchanged --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script> </head>Repeat on every page. If your pages share a header include, put it there once.
Leave your trackers alone. Don't delete them and don't wrap them in your own "if consented" code. The widget holds them back until the visitor agrees.
Options in huOptions (common keys):
| Key | Default | Change it when |
|---|---|---|
blocking |
true |
Almost never. false means scripts run before consent. |
currentLanguage |
"en" |
The page is in another language (two-letter code). |
globalCookie |
false |
Consent should be shared across your subdomains. |
The dashboard snippet may also include other keys (for example Consent Mode defaults, blockingEngine, custom providers). Keep whatever the live snippet gave you.
Colours, wording, consent categories and regions are not set in the page. They live in your published configuration in the dashboard, and anything you add to the page for them is overwritten when the banner loads.
Check it works #
Use a private window, so no earlier consent is remembered.
Your location matters. If region rules are switched on in the dashboard, the banner and the blocking follow the rule for the visitor's region. Some regions may be set to show no banner, or not to block. Test from a location covered by your strictest rule (for example the EU), or confirm region rules are off, before judging checks 1 and 3.
| # | Check | Pass |
|---|---|---|
| 1 | Look at the page | The banner appears on first visit. There is no "Hu-manity PREVIEW — not active consent management" badge (that badge means a demo snippet was installed instead of the real one). |
| 2 | View the page source | The huOptions block and hu-banner.min.js are the first scripts in <head>, with no async or defer, and the page contains no previewMode, forceShow or cnPreview (preview-only keys). For a v2 app, the script src is /v2/hu-banner.min.js. |
| 3 | Before clicking anything | No tracking cookies (for example _ga, _gcl_au, _fbp) in the Application → Cookies panel, and no data hits in the Network tab (for example google-analytics.com/g/collect, facebook.com/tr). |
| 4 | Allow everything (choose the most permissive option; on a Classic banner, then click Save; the New banner has no Save button, each choice saves on click, and if the app has the Reloading setting on, the page reloads once after the choice) | Trackers start running straight away, with no reload needed: their cookies appear and data hits go out. After a reload the banner stays closed and a hu-consent cookie exists. |
| 5 | Another page | Repeat checks 1–3 on an inner page, in a fresh private window, not only the homepage. |
Check 3 is the one that matters for compliance. In the Elements panel, blocked tags show type="javascript/blocked", but cookies and data hits are the real proof, because a script can still inject other scripts.
A tracker's script file may still download before consent. While the page loads, the browser looks ahead in the HTML and starts downloading <script src> files it finds there, even while the Cookie Compliance snippet above them is still running. (Scripts that code adds later are not fetched this way.) So you can see a request for a file such as googletagmanager.com/gtag/js even when the banner is blocking it correctly: the file downloads but never runs. That is not a failure, as long as its cookies and data hits stay absent. To confirm it didn't run, type these in the browser console before choosing anything: typeof google_tag_manager should print "undefined" (Google), and typeof fbq === 'undefined' || !fbq.getState should print true (Meta).
Google, Meta or Microsoft Consent Mode on? Then their loader scripts (such as googletagmanager.com/gtag/js or connect.facebook.net) are allowed to load before consent on purpose. They receive a "denied" consent signal instead of being blocked. With Google Consent Mode, cookieless pings to google-analytics.com that carry the denied state are expected too. Seeing those requests is correct. Judge check 3 by the cookies: none of their tracking cookies should appear before the visitor accepts.
Troubleshooting #
The status check is https://designer-api.hu-manity.co/api/designer/user-design-live/?AppID=YOUR_APP_ID (public, no sign-in). Any 400 means the configuration is not live yet.
| Symptom | Cause | Fix |
|---|---|---|
| The script loads but no banner shows | The app is not published. | Click Publish Now in the dashboard, then copy the snippet again from Integrations → Manual Integration. |
| Status check returns 400 "App is not published yet" | Same. | Same. |
| Status check returns 400 "App does not exist" or "App was deleted" | The AppID in the snippet is wrong, or the app is gone. | Do not publish. Copy the snippet for the current app from Integrations → Manual Integration. |
The script path does not match the status check's WidgetVersion ("v2" needs /v2/hu-banner.min.js) |
The snippet was copied before publishing, or before an engine switch. | Pasted snippet: copy it again from Integrations → Manual Integration after publishing (with MCP: call install.getSnippet again). WordPress plugin: never add this snippet; use the plugin fix in step 0 of verify-install. |
No banner, and the console shows [hu] status banner-hidden:gpc |
The browser sends Global Privacy Control. | Nothing: this is expected. Re-test in a fresh profile without Global Privacy Control. |
| No banner in Playwright, Puppeteer or Selenium | The widget does not run in automated browsers. | See the first gotcha below. |
Gotchas #
- Automated browsers see no banner. The widget deliberately doesn't run when the browser reports that it is automated (
navigator.webdriver), or when the user agent looks like a bot, includingHeadlessChrome. If you check with Playwright, Puppeteer or Selenium, hidenavigator.webdriverand use a normal desktop user agent, or you'll get a false "banner missing". - "Combine" or "minify JS" optimisers (in hosting panels or caching plugins) can merge or move the snippet. Exclude both tags from them.
- Remove any other consent banner. If the site already has a hand-made cookie popup or another consent tool, take it out. Two banners give visitors two conflicting choices.
- Only install your own AppID. Consent recorded by the snippet is logged against that app, together with the website's domain it came from (not the individual page address).