The single most common question we get about payment page scripts is some version of this one: can I put an integrity hash on the Google Ads tag? The short answer is no, and it is not a mistake on your part or on Google's. This page explains why the answer is no, which scripts you can hash, and what to do about the ones you cannot. It also covers what PCI DSS actually asks for, which is not "use Subresource Integrity everywhere".

What Subresource Integrity does

Subresource Integrity, usually written SRI, is an attribute you put on a script tag carrying a cryptographic hash of the file you expect. The browser downloads the file, hashes it, and compares. If the two do not match, the browser refuses to run the script. It is a genuinely good control and it is one line of HTML. If a CDN is compromised and starts serving a modified copy of a library you reference, SRI stops that copy from executing in your customers' browsers. It stops it before any of your own monitoring or logging gets a chance to notice.

Why it cannot work on the Google tags

SRI requires that the file at a given URL never changes. That is the whole mechanism: a hash of a moving target is a hash that will be wrong tomorrow. The Google tag scripts, meaning gtag.js from googletagmanager.com, the GTM container script, and the Google Ads conversion tags that come with them, are deliberately not fixed files. Google updates them on its own schedule to ship features and fixes. The GTM container script is generated from your container configuration, so it changes whenever anybody on your team publishes a container version. Put an integrity attribute on any of them and the tag works until the next update and then stops, silently, in every visitor's browser. Google does not publish SRI hashes for these scripts and is clear that it does not intend to. The same is true of most third-party tags: Meta's pixel, chat widgets, session recorders, consent managers, and the analytics libraries all ship updates without telling you.

Self-hosting the tag is not the fix it looks like

The obvious next thought is to download gtag.js, serve it from your own domain, and hash that. It does work in the narrow sense that the hash will hold. What you have actually done is take ownership of a file you do not maintain, freeze it at whichever version you downloaded, and sign up to notice by hand when it needs updating. When Google changes something server-side that the frozen copy does not understand, your conversion tracking degrades quietly, and the thing that broke is now a file with your name on it. It also does nothing for the scripts that gtag.js loads at runtime, which are still coming from Google. There are legitimate reasons to self-host a tag, mostly to do with ad blockers and consent, but PCI script integrity is not a good one.

Which scripts you can hash, and should

SRI is worth applying wherever the URL is genuinely fixed, and there are usually more of those on a checkout than people expect. A library pulled from a package CDN with a version number in the path is exactly the case SRI was designed for. Think of a pinned jQuery, a date picker, or a card formatting helper from cdnjs, jsDelivr or unpkg. So is any script you build and deploy yourself with a content hash in the filename. Those URLs are immutable by construction, so the hash is stable, and a package CDN serving different bytes under an unchanged versioned URL is one of the clearest supply chain signals there is. If you hash nothing else on your checkout, hash those.

The crossorigin attribute, which is where this goes wrong

One detail catches people out and it is worth stating plainly. An integrity attribute on a script from another domain also needs a crossorigin attribute, normally crossorigin="anonymous". Without it the browser cannot read the response in a way that lets it verify the hash, so it blocks the script instead of checking it. The symptom is a broken checkout, and because the cause is one missing word in a tag you just edited for security reasons, it usually gets diagnosed as something else. Test in a real browser with the console open after you add any integrity attribute, and test the checkout end to end, not just the page load.

Content Security Policy does the other half

SRI answers "is this the code I expected". A Content Security Policy answers "is this script allowed to be here at all". They are different questions and you want both. A CSP lists the origins a page may load scripts from, so an injected tag pointing at an attacker's domain never executes, whatever it contains. That covers the case SRI cannot: a script you never referenced, added to the page after the fact. What CSP does not do is notice when a script you did allow starts behaving differently, because the origin is still on the list. Between them SRI and CSP cover new sources and changed files. Neither covers an allowed vendor shipping malicious code under an unchanged URL, and that is the one that has actually happened to real merchants.

Using a CSP with Google Tag Manager

You can run GTM under a strict CSP, and this is the practical answer for the tags you cannot hash. Google supports a nonce-based approach. Your server generates an unguessable random value for each response, and puts it in the script-src directive and on the GTM snippet's script element. Tag Manager then propagates that nonce to the scripts it injects. Google documents this in its tag platform security guide. Two things to know before you start. Certain GTM features want more than the basics: Custom JavaScript variables, Preview mode, and the specific hosts for Google Analytics 4, Google Ads and Floodlight. Budget time for finding those in report-only mode first. And a nonce authorizes whatever your container publishes, so it moves the trust boundary to your GTM container permissions rather than removing it. Who can publish a container version becomes a real access control question at that point.

What PCI DSS actually asks for

Requirement 6.4.3 asks for three things about scripts on a payment page: a method to confirm each script is authorized, a method to assure the integrity of each script, and an inventory with a written business justification for each one. It does not name Subresource Integrity, or CSP, or any product. It names outcomes. That matters here, because a merchant who reads "assure integrity" as "add an integrity attribute to every script" ends up either breaking their tags or concluding, wrongly, that they cannot comply. Integrity assurance can be an SRI hash where the URL is fixed. It can be a CSP that constrains what may load. It can be a monitoring mechanism that records the content of each script and tells you when it changes. For most real checkouts it is a combination, and the combination is what you write down.

So what do you do about gtag.js

Here is the honest answer, in order. First, ask whether the tag needs to be on the payment page at all. A great many advertising and analytics tags exist on a checkout because someone put the container on every page in 2019 and nobody has revisited it. GTM lets you scope triggers, so firing the marketing tags everywhere except the checkout is often a ten minute change and it removes the problem rather than managing it. Second, if it does need to be there, constrain it: a CSP with a nonce, and container publish rights limited to people you would trust with a card form. Third, write the justification, because the requirement asks for one and this is a script whose presence genuinely needs a sentence of explanation. Fourth, watch it, because the one control that does apply to a script you cannot hash is noticing when its contents change. That is requirement 11.6.1, and it is the reason that requirement sits next to this one.

Watching a script that is meant to change

This is the part that is easy to get wrong in the other direction. If you monitor script contents and alarm on every difference, Google Tag Manager and the Cloudflare beacon will alarm most weeks. Nobody will read the diff, and within a month your team will be clearing the alerts without looking. That habit is more dangerous than not monitoring at all, because it produces a record that says somebody reviewed a change they did not review. The workable answer is to record every change either way, and to decide in advance, in writing, which vendors ship routine updates you are accepting. Our monitoring does this with a per-script setting. You can say that a named vendor updates its own script on its own schedule, and that routine body changes to it should not raise a finding. What that setting never suppresses is a new host, a new subdomain, or an integrity attribute disappearing, because a new name is not a routine update no matter who the vendor is. The setting is recorded as part of the justification, with a date and a person on it. An assessor who sees no change alerts for Google can see who decided that, and when.

A short checklist

Hash the scripts with fixed, versioned URLs and remember the crossorigin attribute. Do not try to hash the vendor tags that update themselves. Put a CSP in front of the page and use a nonce for GTM if you use GTM. Take the marketing tags off the checkout wherever the business can live without them, which is more often than people assume. Write the justification for everything that stays. Then monitor the page so that a change to a script you could not hash is something you find out about in a day rather than in a forensic report. Our free payment page check will show you which scripts on your checkout carry an integrity attribute and which come from a domain other than yours. That is a reasonable place to start the list.