Skip to main content

Content Security Policy (CSP) and the Albacross scripts

Written by Oskar Johnson Hägglund

If your website sends a Content Security Policy (CSP), the browser only loads scripts, images and connections from the sources the policy lists. Anything else is blocked quietly: the page keeps working, but the Albacross script never runs — or runs and can't send anything — and nothing in Albacross tells you why.

This article lists what the tracking script and the Reveal script need from a CSP, the exact browser messages each problem produces, and how to fix them.

Note: the Check installation button in Albacross only looks for your client ID on the page. It reports the tracking code as installed even when the policy blocks the script file or the events it sends, so a green check isn't proof that visitors are being recorded. Use the console and Network checks at the end of this article instead.

Is CSP what's blocking you?

Open the page in Chrome, open DevTools (F12, or ⌥⌘I on a Mac), go to the Console tab and filter on Content Security Policy. A blocked resource produces a red message naming the URL that was blocked and the directive that blocked it:

⛔️ Loading the script 'https://serve.albacross.com/track.js' violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback. The action has been blocked.

Older Chrome versions start these messages with Refused to load… or Refused to execute…. Firefox and Safari word them differently, but they always name the blocked URL and the directive.

To see the policy itself, open the Network tab, select the page's own request and look for a Content-Security-Policy response header — or search the page's <head> for a <meta http-equiv="Content-Security-Policy"> tag.

Two things that look like CSP but aren't:

  • net::ERR_BLOCKED_BY_CLIENT on track.js or e.gif is an ad or tracker blocker in the visitor's browser. No CSP change fixes it.

  • A snippet that shows up in the page source as <script type="text/plain"> is being held back by a consent tool until the visitor accepts cookies. Also not CSP.

What the tracking script needs

The tracking code you copy from Albacross is two script tags:

<script>window._nQc="YOUR_CLIENT_ID";</script> <script async src="https://serve.albacross.com/track.js"></script>

The first is an inline script. The second loads track.js from serve.albacross.com. Once loaded, the script sends visitor events as a small image (a GIF) to new-collect.albacross.com. It makes no other requests and uses no eval, styles, frames or workers.

What

Directive

Value to allow

track.js

script-src (or script-src-elem)

https://serve.albacross.com

The inline window._nQc="…"line

script-src

a nonce, a hash or 'unsafe-inline' — see Allowing the inline snippet below

The events pixel, e.gif

img-src

https://new-collect.albacross.com

A minimal policy that keeps everything else locked to your own origin. The URL allows the file; the 'sha256-…' value allows the one inline line — Chrome prints the exact hash for you, see Allowing the inline snippet:

Content-Security-Policy: script-src 'self' https://serve.albacross.com 'sha256-PASTE_THE_VALUE_FROM_THE_CONSOLE'; img-src 'self' https://new-collect.albacross.com

If your site already generates a nonce for its scripts, 'nonce-…' takes the place of the hash.

If your policy only sets default-src, every directive above falls back to it — so default-src 'self'on its own blocks all three.

What the Reveal script needs

The Reveal script is separate from the tracking script: it makes the visiting company available to your own page code. The snippet you copy from Albacross is one inline <script> block containing your API key. It adds a script tag that loads reveal.js, which then calls the Reveal API with fetch.

What

Directive

Value to allow

reveal.js

script-src (or script-src-elem)

https://serve.albacross.com

The inline snippet

script-src

a nonce, a hash or 'unsafe-inline'

The call to the Reveal API

connect-src

https://reveal.api.albacross.com

Content-Security-Policy: script-src 'self' https://serve.albacross.com 'sha256-PASTE_THE_VALUE_FROM_THE_CONSOLE'; connect-src 'self' https://reveal.api.albacross.com

The Reveal script doesn't send the events pixel, so it needs no img-src entry. If you run both scripts, combine the two policies.

Allowing the inline snippet

The URL in script-src covers the file we serve — track.js or reveal.js — and a plain URL is exactly the right way to allow it. It cannot cover the first tag, because that tag has no URL: it is code written straight into the page (window._nQc="…"), and a CSP treats every inline script as untrusted unless the policy vouches for it. There are three ways to vouch for it. Pick by what your site already has.

Your policy already contains 'nonce-…' → put the same nonce on our tags.

A nonce ("number used once") is a random token your server generates afresh for every page load. It appears in two places: in the header, as script-src 'nonce-abc123', and as an attribute on every script tag the page trusts, <script nonce="abc123">. The browser runs an inline script only when the two match. Because the value has to change on every response, a nonce can only come from server-side code, a framework's CSP middleware or a CMS plugin that already does this — it cannot be typed into a static page. If your site has one, give the Albacross tags the same nonce attribute your other scripts carry — on both tags, because a nonce policy usually doesn't list our host, and a policy with 'strict-dynamic' ignores host allowlists altogether:

<script nonce="abc123">window._nQc="YOUR_CLIENT_ID";</script> <script nonce="abc123" async src="https://serve.albacross.com/track.js"></script>

For Reveal, put the nonce on the snippet's single <script> tag. Under 'strict-dynamic' the reveal.js tag it creates inherits the trust; without 'strict-dynamic', keep https://serve.albacross.com in script-src.

No nonce on your site → allow the hash of the inline line.

A hash is the static alternative. The browser computes the SHA-256 of the exact text between <script> and </script> and runs the script if that value is in the policy — nothing has to be generated per page load, so it works on any site, including static ones. Chrome prints the hash it wants in the error message (a hash ('sha256-…')). Add exactly that value to script-src:

script-src 'self' https://serve.albacross.com 'sha256-PASTE_THE_VALUE_FROM_THE_CONSOLE'

The hash covers the exact text between <script> and </script>, so it changes if the client ID or API key changes, or if someone adds a space. It does not change when we release a new version of track.js or reveal.js.

Either way, you can avoid the inline tag altogether. Any script that already runs on the page can set the global before our file loads, and then only the URL needs allowing. For the tracking script:

<script src="/js/site.js"></script>   <!-- site.js contains:  window._nQc = "YOUR_CLIENT_ID"; --> <script async src="https://serve.albacross.com/track.js"></script>

For Reveal, set window._nQa = "YOUR_API_KEY" in your own script and load the file with a plain tag instead of the snippet:

<script async src="https://serve.albacross.com/reveal.js"></script>

This form is also the one to use on sites that enforce Trusted Types (see message 6 below).

'unsafe-inline' is a fourth option. It works on a policy that has no nonce or hash, but browsers ignore 'unsafe-inline' as soon as the same directive contains a nonce or a hash, so adding it to such a policy changes nothing. Chrome says so in the message: Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list.

Error messages and their fixes

The messages are Chrome's, verbatim, shown the way the console shows them — red, in the Console tab. Search the console for any part of one.

  1. Our host isn't allowed in script-src.

    Loading the script 'https://serve.albacross.com/track.js' violates the following Content Security Policy directive: "script-src …"

    (or …/reveal.js). Add https://serve.albacross.com to script-src, or to script-src-elemif you set that one. If the policy uses 'strict-dynamic', hosts are ignored — put the nonce on the tag instead.

  2. The inline line is blocked.

    Executing inline script violates the following Content Security Policy directive … Either the 'unsafe-inline' keyword, a hash ('sha256-…'), or a nonce ('nonce-...') is required to enable inline execution.

    Use one of the three options under Allowing the inline snippet.

  3. Check installation says the code is installed, but no companies show up.

    Loading the image 'https://new-collect.albacross.com/e.gif?s=JSCollector…' violates the following Content Security Policy directive: "img-src …"

    The script runs, but the events it sends are blocked. Add https://new-collect.albacross.com to img-src. This is the case Check installation cannot see — it only finds the client ID on the page.

  4. Reveal: AlbacrossReveal.company stays empty and AlbacrossRevealOnFail fires with TypeError: Failed to fetch.

    Connecting to 'https://reveal.api.albacross.com/company' violates the following Content Security Policy directive: "connect-src …"
    Fetch API cannot load https://reveal.api.albacross.com/company. Refused to connect because it violates the document's Content Security Policy.

    Add https://reveal.api.albacross.com to connect-src. This isn't a CORS problem — the Reveal API accepts requests from any origin.

  5. http:// where you expected https://.

    Loading the script 'http://serve.albacross.com/reveal.js' violates the following Content Security Policy directive: "script-src …"

    The Reveal snippet loads reveal.js over the same scheme as the page, so on an http:// page it asks for http://serve.albacross.com/reveal.js, which an https://-only allowlist blocks. Serve the page over HTTPS. If you can't, list serve.albacross.com without a scheme.

  6. Trusted Types.

    This document requires 'TrustedScriptURL' assignment.
    Failed to set the 'src' property on 'HTMLScriptElement'

    Your policy has require-trusted-types-for 'script'. The Reveal snippet creates its script tag from JavaScript, which Trusted Types forbids. Set window._nQa in an existing script and load reveal.js with a plain <script async src> tag (the third option above), or use the Google Tag Manager template. The tracking script isn't affected by Trusted Types.

  7. The messages are blue, not red, and end with:

    The policy is report-only, so the violation has been logged but no further action has been taken.

    Your site sends Content-Security-Policy-Report-Only. Nothing is blocked yet, but the same violations will block the scripts the day the policy is enforced. Add the directives now.

  8. Subresource Integrity.

    Subresource Integrity: The resource 'https://serve.albacross.com/track.js' has an integrity attribute, but the resource requires the request to be CORS enabled to check the integrity, and it is not.

    Or, if the tag also has crossorigin, a CORS error about a missing Access-Control-Allow-Originheader. Someone added an integrity attribute to our script tag. Our script files are updated in place and aren't served with CORS headers, so Subresource Integrity can't work with them. Remove the integrity and crossorigin attributes.

  9. net::ERR_BLOCKED_BY_CLIENT, or a snippet that appears as type="text/plain".

    Not CSP. See Is CSP what's blocking you? above.

Tag managers, platforms and plugins

  • Pasted snippet — before </body> or </head>, or through Wix, Squarespace, Webflow, or Shopify's theme editor or Preferences: everything above applies as written. On a hosted platform you usually can't change response headers yourself, so if your pages carry a CSP it's the platform's, and you extend it through the platform's own settings or support.

  • Google Tag Manager with our gallery templates — Albacross Tracking and Albacross Reveal in the Community Template Gallery. The templates set the global and load the file through GTM's sandboxed APIs, so the Albacross part needs no inline permission. Your policy still needs script-src https://serve.albacross.com, img-src https://new-collect.albacross.com and, for Reveal, connect-src https://reveal.api.albacross.com, plus what GTM itself needs — a nonce on the container snippet and https://www.googletagmanager.com — as described in Google's CSP guide. On a strict-CSP site this is the easiest route.

  • Google Tag Manager with a Custom HTML tag — what the Google Tag Manager tab on the Website Tracking page in Albacross, our platform install guides and the Reveal script guide describe. GTM injects the pasted snippet as an inline script, so message 2 applies and GTM's own nonce doesn't cover it. On a site with a strict CSP, use the gallery template instead. Google's guide also notes that Custom JavaScript variables need 'unsafe-eval'; our scripts don't.

  • Adobe Launch / Adobe Tags, Custom Code (HTML) action — see How do I install the Albacross tracking code with Adobe Launch. Adobe needs inline scripts allowed, either by the nonce configured in the Core extension or by 'unsafe-inline', as described in Adobe's CSP guide. The <script src> inside the snippet still needs script-src https://serve.albacross.com, and the pixel needs img-src https://new-collect.albacross.com.

  • WordPress, Joomla! and Drupal plugins — the plugins print the same inline snippet, so the pasted-snippet rules apply. The nonce has to come from your CSP plugin or theme. A hash works too, because the snippet text is fixed for a given client ID.

  • GA4 and Adobe Analytics with Reveal — both guides on docs.albacross.com start by pasting the Reveal snippet into the page, so the Reveal rules apply. The GTM container template for GA4 and the Adobe Launch rule only read window.AlbacrossReveal; they don't load our script. GA4, GTM and Adobe have CSP requirements of their own; see their documentation.

Checking that it works

After changing the policy, reload the page with DevTools open:

  1. Console, filtered on Content Security Policy: no messages naming albacross.com.

  2. Network tab, filtered on albacross:

    • track.js from serve.albacross.com with status 200.

    • Within a few seconds of the page loading, a request to new-collect.albacross.com/e.gif?s=JSCollector… with status 200. That's the first batch of events; more follow as the visitor interacts with the page and when they leave it.

    • For Reveal, a request to reveal.api.albacross.com/company. Any status means the policy let it through — a 403 means the API key is wrong or the visitor's company isn't known, which is a different problem.

  3. Check installation in Albacross still only confirms that the client ID is on the page, so don't use it as the final test.

Once those requests go through, visiting companies appear in Albacross as they're identified.

Did this answer your question?