Free tool · no sign-up

SHA-256 tester for enhanced conversions

Check whether Google Ads and Meta receive your customer’s hashed email and phone number, or a hash of an empty field or undefined that no conversion will ever match. Without BigQuery you check a hash from GTM Preview. With BigQuery you check every stored hash in one query.

  • 107 empty and test values for email and phone
  • Normalisation rules from the Google Ads, GA4 and Meta docs
  • Online version and a BigQuery SQL query
  • Your data never leaves your browser

Author: Marcin Miłowski, GA4, GTM and BigQuery consultant · Updated: October 2026

Where are the hashes you want to check?

Reference list

SHA-256 hashes of empty and invalid values

If the hash from your website is on this list, your tag sends Google Ads or Meta an empty or random value instead of customer data. The fastest check is Ctrl+F (Cmd+F on a Mac): paste the start of your hash.

Email field

Hashed valueSHA-256Where it comes from
(empty string)e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855empty variable: the tag fired before the form stored the email
(space)36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068a space in an empty form field
null74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90bthe variable is null
NULLfb329000228cc5a24c264c57139de8bf854fc86fc18bf1c04ab61a2b5cb4b921NULL from a database or CRM, not converted to lowercase
undefinedeb045d78d273107348b0300c01d29b7552d622abbc6faf81b3ec55359aa9950cthe variable does not exist and the code turned it into text
none140bedbf9c3f6d56a9846d2ba7088798683f4da0c248231336e6a05679e4fdfeNone from server-side code, after normalisation
nan9b2d5b4678781e53038e91ea5324530a03f27dc1d0e5f6c9bc9d493a23be9de0JavaScript NaN after normalisation
05feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9the number zero instead of an empty field
falsefcbcf165908dd18a9e49f7ff27810176db8e9f63b4352213741664245224f8aaa boolean instead of data
[object object]c9a560c74de368dcd2e6ac5544ad12a9afd32584b71a52147303479f726e0408a JavaScript object turned into text, after normalisation
{{email}}2fa568238f6d6aa0d84b7d66ec13b95eef51f803f1fdc5b8c54e475574ee6de3an unfilled variable in a template or tag
email82244417f956ac7c599f191593f7e441a4fafa20a4158fd52e154f1dc4c8ed92the field name instead of its value
e-mail39e0749da40f625e3ac47efdf64b79fa28f8aad01f3c2746176ea4a03a9e98e1the field label instead of its value
your email4971024aa5990614e2b59315e61154b9e6200060679060147a3f16db779eddafplaceholder text from the form
test@test.comf660ab912ec121d1b1e928a0bb4bc61b15f5ad44d5efdc4e1c92a25e99b8e44aa test address from the form or the code
test@example.com973dfe463ec85785f5f95af5ba3906eedb2d931c24e69824a89ea65dba4e813ba sample address from documentation or code
(empty string, hashed twice)cd372fb85148700fa88095e3492d3f9f5beb43e555e5ff26d95f5a6adc36f8e6an empty variable hashed twice

Phone field

Hashed valueSHA-256Where it comes from
(empty string)e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855empty variable: the tag fired before the form stored the phone number
+4423ead39c5af7f86bc6b2466a8ae10442c3663bdc4422723bc54def68caf184f6only the country code added to an empty field (Google Ads)
4471ee45a3c0db9a9865f7313dd3372cf60dca6479d46261f3542eb9346e4a04d6only the country code added to an empty field (Meta)
+44undefined482c65fe3d97b214c3199c56dcf81abbe91155505348544f70d5fe11f9303fd1country code added to a variable that does not exist
44undefined53e343855cdbab3a2354c32bd5ae7327c22439ba1f76f9db5d8e1177ec26addethe same with Meta normalisation
+44nulle5ce5543605c8701eaab31b25e671249db6740e4efdbba2fbbe92d3af93fbb52country code added to a null variable
+1c59dc4e44ff99288156d4dff2168f6ac7ddee6b1fc7ccc0754656ffaa6d351eaonly the US/Canada country code added to an empty field
05feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9the number zero instead of a phone number
12345678915e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225a test number
+441234567893d84a3838599719df7deacc7fb91903bde5430a8c0e007c3eba93bce0c69c5a2a test number with the country code
phone45569da57f4b7bf472d7a864ef4781451cae6383fee9fb0ae40c59aa1ce475b7the field name instead of its value

The tester recognises more values than this list, including other country codes, uppercase spellings and values hashed twice.

How to use it

Two paths: without BigQuery and with BigQuery

Without BigQuery you check one test form in a few minutes. With BigQuery you see what percentage of all your conversions sends a broken hash.

I don’t have BigQuery

  1. Submit a test form. Turn on GTM Preview (and server-side GTM Preview, if you have it) and submit a form or an order with your own test email and phone number.
  2. Copy the hash. In GTM Preview, click the conversion event and open the user data variable. In server-side GTM check the user_data field. For Meta, use the Test Events tab in Events Manager.
  3. Choose email or phone, and the platform. Phone normalisation differs between Google Ads and Meta, so the result depends on this choice.
  4. Paste the hash and the form data. The tester shows whether the hash matches and, if it doesn’t, whether it is an empty value or a normalisation error.

I have BigQuery

  1. Find the table with the hashes. Hashes are only in BigQuery where you store them yourself: in server-side GTM logs, an offline conversion upload table or CRM data.
  2. Fill in the tester fields. Choose email or phone, the platform, the table and the hash column. If the same table holds the plain data, add its column too.
  3. Run the SQL in the BigQuery console. The query uses the SHA256 and TO_HEX functions. Add a date filter so you don’t scan the whole history.
  4. Read the result. You see the number and percentage of rows: no hash, empty value, wrong format, uppercase, hash not matching the data, and valid hashes.

Where empty hashes come from

6 mistakes that send Google Ads and Meta a hash instead of a customer

In Preview, the hash of an empty field looks just like a valid email: 64 characters of digits and letters. That is why this bug can survive for months, while enhanced conversions report a low match rate.

  • The tag fires too early. A button click trigger fires the tag before the form writes the email to the dataLayer. An empty string gets hashed.
  • Custom code hashes a missing value. The variable returns undefined, and the JavaScript code turns it into the text “undefined” and hashes it.
  • A form in an iframe or a pop-up. GTM on the page cannot see the field in an embedded form, so the variable picks up the field label or nothing.
  • Double hashing. A shop or CRM plugin already sends a hashed email, and the tag hashes it a second time.
  • No normalisation. Uppercase letters, spaces, dots and a plus suffix in Gmail, or a phone number without the country code, produce a hash of different data than what Google or Meta know.
  • Wrong hash encoding. Uppercase letters in the hash, or Base64 instead of hexadecimal (hex), when you hash the data yourself.

From my projects: on a diet catering website, Google Ads reported a critical enhanced conversions error because the data was hashed with SHA-1 instead of SHA-256. On another catering platform, the hashed phone number in E.164 format was missing from the dataLayer. See the case study

Cheat sheet

Normalising email and phone before SHA-256: Google Ads and Meta

Google Ads and Meta normalise data differently, especially phone numbers. Changing a single character changes the whole hash, so the data has to be written exactly as the platform you send it to requires.

StepGoogle Ads and GA4Meta Conversions API
Unhashed dataallowed: the Google tag normalises and hashes it before sendingnot allowed: em and ph must be hashed (the browser pixel hashes on its own)
Emailno leading or trailing spaces, lowercase; for gmail.com and googlemail.com remove dots before the @ and the plus suffix, in other domains remove nothingno leading or trailing spaces, lowercase; do not remove dots
Plus suffixremove it in Gmail (name+test@gmail.com → name@gmail.com), keep it in other domainsthe documentation does not remove it
PhoneE.164 format: plus, country code, no spaces or hyphens, e.g. +447700900123digits only with the country code, no symbols or leading zeros, e.g. 447700900123
Hashif you hash it yourself: SHA-256 in hex, 64 characters 0-9 and a-f, lowercase; a Google tag that receives raw data hashes it itself in web-safe Base64SHA-256 in hexadecimal (hex): 64 characters 0-9 and a-f, lowercase

Email normalisation examples for Google Ads and GA4

Email enteredAfter normalisationStart of the hash
JOHNsmith@company.comjohnsmith@company.comb339b4e8fe19…
j.o.HNSmith+test@company.comj.o.hnsmith+test@company.com934050e71cf4…
john..smith@gmail.comjohnsmith@gmail.com3586de92bb36…
johns.m.ith@googlemail.comjohnsmith@googlemail.comb1dd4aa5ffe1…
J.o.HNSmith+test@gmail.comjohnsmith@gmail.com3586de92bb36…

An inconsistency in Google’s documentation that few people know about. The Google Ads help page on enhanced conversions with the Google tag and the GA4 documentation only tell you to remove dots from Gmail addresses. The Google Ads API documentation goes further: for gmail.com and googlemail.com you also have to remove the plus suffix, or the hashes will not match. The example from that documentation: Jane.Doe+Shopping@googlemail.com → janedoe@googlemail.com. I found this inconsistency by comparing Google’s documents, which is why the tester applies the full rule from the API documentation.

Dots and the plus suffix are only removed in Gmail addresses. In other domains, including company domains, the dot and the plus are part of the address, so keep them. Sources: Google Ads API, Email address formatting for enhanced conversions; GA4, Send user-provided data using Measurement Protocol; Google Ads, Set up enhanced conversions for web using the Google tag; Meta, Customer Information Parameters.

How it works

SHA-256: one-way encryption of customer data

SHA-256 is a hash function: it turns an email or phone number into a 64-character string from which the data cannot be recovered. That is why people call it one-way encryption. A regular cipher has a key and can be decrypted, while a hash has no key and only works in one direction.

Google and Meta require SHA-256 because it lets them compare customer data without receiving it in plain text. Conversion matching works like this:

  1. Your website hashes the customer’s email

    The address john@gmail.com always produces the same hash, 142d78e466ca…. Only this hash reaches Google or Meta.

  2. The platform hashes its users’ addresses

    Google and Meta compute SHA-256 from account addresses, using the same normalisation rules described in the cheat sheet above.

  3. It compares hashes, not emails

    When the hashes are identical, the conversion is attributed to the person who clicked the ad. The platform never gets a list of plain-text addresses from you.

This leads to the most important rule: changing a single character changes the whole hash. john@gmail.com gives 142d78e466ca…, while John@gmail.com gives 258d4f75ef1d…. Without identical normalisation on both sides, matching fails. For the same reason the hash of an empty field is always the same, so the tester recognises it by its value.

A hash is not anonymisation. It cannot be reversed, but anyone can hash a known address and compare the result. That is why, under the GDPR, a hashed email is still personal data and needs a legal basis and ad_user_data consent in Consent Mode.

Ready-made implementation

A correct email and phone hash for Google and Meta, without guessing the rules

Based on the rules in this tester, I built ready-made normalisation and hashing of user data for Google Ads, GA4 and Meta CAPI. It covers the Gmail rule from the Google Ads API documentation, the different phone formats for Google and Meta, and a block that stops the hash of an empty field from being sent. It comes in two versions.

Version 1 · better and more reliable

Developer guidelines for Jira

A ready Jira ticket: dataLayer specification, email and phone normalisation rules for Google and Meta, test data with expected hashes and acceptance criteria. A developer implements the hashing in the website’s code.

Why it’s better: the website passes the data at the moment the form or order is submitted, so it does not depend on how the form looks or on the tag order in GTM.

Version 2 · no developers needed

Ready GTM container (JSON file)

You import the file into Google Tag Manager and get variables that normalise and hash email and phone separately for Google and Meta. Implement it yourself or with me.

When to choose it: when developers have no time or are not available, and enhanced conversions need fixing now.

Combining both versions works best. Until the developer ships the change, make the GTM-side fix right away so you don’t lose conversions. Once the developer adds the hashes to the dataLayer, the container switches to them automatically, with no changes in GTM.

Code only

€25 net

Both versions in one package: the GTM container (JSON file) with setup instructions, and a Jira ticket with JavaScript and PHP code, test data and acceptance criteria.

Full implementation

from €125 net

I implement the container in your GTM, connect it to Google Ads, GA4 and Meta, then check the hashes in GTM Preview and in the tester. The price depends on how your website and forms are built.

Tell me whether you need the code only for €25 net, or the full implementation.

FAQ

Questions about hashing email and phone for enhanced conversions

Does Meta accept an unhashed email and phone number?

Not in the Conversions API. The em and ph fields must be SHA-256 hashed, and Meta rejects unhashed contact data. The Meta Pixel in the browser (advanced matching) hashes the data itself.

Should I send Google Ads the email in plain text or hashed?

Google Ads accepts both. The Google tag normalises and hashes unhashed data itself before sending it to Google. Keep in mind that a SHA-256 hash is pseudonymisation, not anonymisation: under the GDPR a hashed email is still personal data. In both cases you need a legal basis and ad_user_data consent in Consent Mode. Every script on the page can see a plain email in the dataLayer, so limit where it appears. Agree on the details with your lawyer or data protection officer.

Should I remove dots and the plus suffix from an email address?

Only in Gmail addresses. For Google Ads and GA4, in the gmail.com and googlemail.com domains remove dots before the @ sign and the plus suffix: j.ohn+test@gmail.com becomes john@gmail.com. In other domains remove nothing, because j.ohn@company.com and john@company.com can be two different mailboxes. The Google help page and the GA4 documentation only mention dots; removing the plus suffix is described only in the Google Ads API documentation. Meta removes neither dots nor the suffix, it only requires lowercase and no spaces.

Hex or Base64 hash?

If you hash the data yourself, before passing it to the tag or to an upload, use SHA-256 in hex: 64 characters 0-9 and a-f. That is what Google Ads, GA4 and Meta require. Base64 appears when you pass raw data to the Google tag: the tag hashes it itself and encodes it in web-safe Base64. The tester recognises both and compares them with the form data.

Can you read an email from a SHA-256 hash?

Not directly. SHA-256 works one way, so the address cannot be mathematically recovered from the hash. However, anyone can hash a known address and compare the result, which is why a hashed email is still personal data under the GDPR.

How does phone normalisation differ between Google Ads and Meta?

Google Ads requires E.164 format with a plus, e.g. +447700900123. Meta requires digits only with the country code, without a plus or leading zeros, e.g. 447700900123. The same data therefore gives two different hashes.

Why is my hash e3b0c442…b855?

That is the SHA-256 of an empty string. The tag hashed an empty variable because it fired before the form wrote the data to the dataLayer, or the GTM variable points to the wrong field. Google Ads and Meta will not match such a conversion to any user.

Can I use an email hash as the user_id in GA4?

No. Google requires that the user ID does not contain information a third party could use to identify the user. A SHA-256 hash of an email without an extra key is easy to match to a known address. Use the internal customer ID from your own system as the user_id.

Where do I find the hash my website sends?

In GTM Preview, select the conversion event and check the user data variable. In server-side GTM, open the event in Preview and check the user_data field or the outgoing request to Meta. In BigQuery you will only find hashes in tables you write them to yourself, such as server-side GTM logs or an offline conversion upload.

Does a green result mean everything works?

Not quite. It means the hash has a valid format and is not one of the known empty values. To be sure it is the right data, enter what you typed in your test form into the tester.

Does the tester have a check limit?

Without entering your email you can check a hash 3 times a day on one device. Once you enter your email, the tester has no limit. The BigQuery path has no limit, because you run the SQL query yourself.

Does the tester send the data I enter to a server?

No. Hashing and comparison happen in your browser, and the site statistics only receive the number of hashes checked, never their content. The only thing that reaches the server is your email address, if you choose to enter it to remove the limit. For testing, use your own test email address and phone number.

The tester found an empty hash? I will help you fix it

In a 30-minute consultation we check together where the tag gets its data, and agree what to fix in the dataLayer, GTM or server-side GTM so Google Ads and Meta match conversions to customers.

Ta strona jest dostępna również po polsku.

Przejdź do wersji polskiej