# Cloudflare Email Decoder (data-cfemail)

> Decode Cloudflare email protection in your browser: paste a data-cfemail hex, a /cdn-cgi/l/email-protection link or raw HTML and read the address.

[Home](https://quanticdata.io/)/[Free tools](https://quanticdata.io/tools/)/Cloudflare email decoder

# Cloudflare email decoder

Paste the hex of a `data-cfemail` attribute, a `/cdn-cgi/l/email-protection#…` link or a whole block of HTML, and this Cloudflare email decoder shows the address behind every `[email protected]` placeholder. The first byte is the key; every byte after it, XOR the key, is one character. Everything runs in your browser.

By [Aldo Morese](https://quanticdata.io/about/), founder of QuanticData · Published Oct 7, 2026

Hex string, protection link or HTML https://www.zkdental.it/cdn-cgi/l/email-protection#73555042434648555042424348555042434148555042424248555045474855504241414855504243444855504243434855504243424855504242434855504242454855504a4448555042434b485550474548555042434648555042424548

Paste anything: every `data-cfemail` attribute and every email-protection link in the text is decoded. A bare hex string works too, one per line. Real examples from Cloudflare-protected pages, 7 October 2026:

Decoded

| # | Found in | Key | Address | Notes |
| --- | --- | --- | --- | --- |

### Show the bytes of the first payload

```

```

Hundreds of pages to read? Our [web scraping API](https://quanticdata.io/web-scraping-api/) decodes Cloudflare-protected addresses on every page it fetches, with no browser and no JavaScript. [See the request and the response](/tools/cloudflare-email-decoder/#api).

The whole algorithm on one payload: the first byte is the key, every following byte XOR the key is one byte of the address, and the bytes are read as UTF-8. The same rule decodes the hex after `email-protection#` in a link.

## How Cloudflare email protection works

Email Address Obfuscation is a Cloudflare setting (part of Scrape Shield). When it is on, Cloudflare's edge rewrites every email address in the HTML before the page leaves the server. An address written as text becomes `<span class="__cf_email__" data-cfemail="…">[email protected]</span>`; a `mailto:` link becomes a link to `/cdn-cgi/l/email-protection#…`. Cloudflare also adds a small script, `email-decode.min.js`, that puts the real addresses back in the visitor's browser.

A browser with JavaScript never notices. Anything that reads the raw HTML does: `curl`, Python `requests`, most crawlers and AI agents get the literal `[email protected]`, and the link points to a `/cdn-cgi/` path with no page behind it. On 7 October 2026 Google's own result snippets for Italian dental practices showed `[email protected]` in place of the address, which is what a non-JavaScript reader sees.

## The algorithm in three steps

1. **Split the hex into bytes.** Read the string two digits at a time. The first byte is the key, a number from 0 to 255 that Cloudflare picks for each occurrence.

2. **XOR every following byte with the key.** Each result is one byte of the address.

3. **Read the bytes as UTF-8, then undo the site's own layers.** Some sites mask the address before Cloudflare encodes it: HTML entities (`&#105;&#110;…`), percent-encoding (`%40`, `%20`), a `mailto:` prefix or a `?subject=` tail. Strip those and the address is left.

The worked example below is the payload drawn in the figure, `5a33343c351a3f223b372a363f74393537`, which splits into the bytes `5a 33 34 3c 35 1a 3f 22 3b 37 2a 36 3f 74 39 35 37`:

| Byte | XOR key 0x5a | Character |
| --- | --- | --- |
| `5a` | the key (90) | none |
| `33` | `0x69` | `i` |
| `34` | `0x6e` | `n` |
| `3c` | `0x66` | `f` |
| `35` | `0x6f` | `o` |
| `1a` | `0x40` | `@` |
| `3f 22 3b …` | `0x65 0x78 0x61 …` | `e x a …` |

Sixteen bytes after the key give `info@example.com`. Because the key changes from one occurrence to the next, the same address can appear on a page under several different hex strings: decode first, then remove duplicates.

## Decode it in Python or JavaScript

The tool above is the readable version. In code it is a few lines. Python, standard library only:

```
import html, re
from urllib.parse import unquote

def decode_cfemail(hex_str: str) -> str | None:
    data = bytes.fromhex(hex_str)
    key = data[0]
    text = bytes(b ^ key for b in data[1:]).decode("utf-8", "replace")
    text = unquote(html.unescape(text))          # layers added by the site
    m = re.search(r"[\w.+-]+@[\w-]+(\.[\w-]+)+", text)
    return m.group(0) if m else None

print(decode_cfemail("5a33343c351a3f223b372a363f74393537"))  # info@example.com
```

JavaScript, in a browser or in Node:

```
function decodeCfemail(hex) {
  const bytes = hex.match(/../g).map((h) => parseInt(h, 16));
  const key = bytes[0];
  const raw = Uint8Array.from(bytes.slice(1), (b) => b ^ key);
  return new TextDecoder().decode(raw);
}

decodeCfemail("5a33343c351a3f223b372a363f74393537"); // "info@example.com"
```

To find the payloads in a page, read the `data-cfemail` attribute of every element that has one (it is usually a `span`, sometimes the `a` itself) and the part after `#` in every `href` that contains `/cdn-cgi/l/email-protection`.

Our measurement of 7 October 2026: what the raw HTML of 54 contact and policy pages contained, read with a plain HTTP client and decoded with the rule above.

## What we found on 54 real pages

On 7 October 2026 we ran four Google searches for pages whose snippet showed `[email protected]`: Italian dental practices, law firms, hotels and US dentists. Of the 68 result pages, 54 returned HTML to a plain HTTP request. We decoded every payload in that HTML with the rule on this page.

| Measure | Result |
| --- | --- |
| Pages still serving Cloudflare email protection | 49 of 54 |
| Encoded payloads in those pages | 508 |
| In a `data-cfemail` attribute / in a link with `#hex` | 269 / 239 |
| Payloads that decoded to an address | 504 of 508 |
| Share links with no recipient (only a subject or body) | 4 |
| Addresses masked by the site first (percent-encoding) | 3 |
| Pages with the same address under two or more keys | 40 of 49 |
| Distinct addresses recovered, summed per page | 159 |

Two things follow for anyone parsing these pages. The protection is the same everywhere, so one decoder covers every site that uses it. And deduplicating on the hex string does not work, because 40 of the 49 pages repeated an address under different keys.

## Why your scraper sees [email protected]

The decoding happens in JavaScript after the page loads. An HTTP client that does not run the page's scripts keeps the placeholder, and a parser that only looks for `mailto:` links finds none, because they all point to `/cdn-cgi/`. A headless browser does get the addresses, but it costs a full render for something the HTML already contains. Decoding in the parsing step is cheaper and gives the same result.

## If it is your own site

Site owners usually meet this feature from the other side: SEO crawlers report hundreds of links to `/cdn-cgi/l/email-protection` as broken, because those URLs only work with JavaScript. You can switch Email Address Obfuscation off for the whole zone in the Cloudflare dashboard, or keep it and exclude single blocks by wrapping them in `<!--email_off-->` and `<!--/email_off-->`. Either way, the obfuscation is a speed bump: as this page shows, it is reversible by design.

## Decode at scale: the scraping API does it on every page

Our [web scraping API](https://quanticdata.io/web-scraping-api/) decodes Cloudflare email protection natively, on the plain HTTP tier, with no browser and no JavaScript. It handles `data-cfemail` on a `span`, an `a` or any other tag; `/cdn-cgi/l/email-protection#…` links, relative or absolute; protection links without the `#hex` (the decoded label is used); and the protection URL written as plain text. It also removes the layers sites add before Cloudflare: HTML entities, `%40`-style encoding, the `mailto:` prefix and the `?subject=` tail.

- **Markdown and text:** the address in clear, with a `mailto:` link. On zkdental.it the link went from `[info@zkdental.it](https://www.zkdental.it/cdn-cgi/l/email-protection#7355…)` to `[info@zkdental.it](mailto:info@zkdental.it)`.

- **HTML:** the page byte for byte as served, except the placeholders, which become the address and `href="mailto:…"`.

- **metadata.contactEmails:** every address with its source, in this order: `json-ld`, `cloudflare`, `mailto`, without duplicates.

- **metadata.contactPhones:** the numbers from JSON-LD and `tel:` links, as `{ phone, source }`.

One request, on 7 October 2026, to the contact page of an Italian lighting manufacturer that protects every address with Cloudflare:

```
curl https://api.quanticdata.io/v1/scrape \
  -H "Authorization: Bearer $QD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.reggiani.net/it/contatti/", "format": "markdown"}'
```

```
{
  "type": "response",
  "message": "Extraction successful",
  "payload": {
    "url": "https://www.reggiani.net/it/contatti/",
    "status": 200,
    "engine": "tls",
    "metadata": {
      "contactEmails": [
        { "email": "contact@reggiani.net", "source": "cloudflare" },
        …
      ],
      "contactPhones": [
        { "phone": "00390297070340", "source": "tel" }
      ]
    },
    "content": "…[contact@reggiani.net](mailto:contact@reggiani.net)…"
  }
}
```

That single response carried 59 addresses in `metadata.contactEmails`, every one with `"source": "cloudflare"`, on the plain HTTP tier in 7.3 seconds.

| Setting | Value |
| --- | --- |
| Endpoint | `POST /v1/scrape` (alias `/v1/scraper/extract`) |
| Engine | `auto`: plain HTTP first, a browser only when the page needs it |
| Price | $0.0002 per page, $0.001 when a browser render is needed; failed pages are not billed |
| Many pages | `POST /v1/batch`: a list of URLs in one async job, at $0.0002 per URL, same output per URL |
| Free tier | $2 of free API usage per month, no card |

Collect business contact data only where you have a lawful basis for it: in the EU that means GDPR applies to a named person's work address too. Public company addresses used for B2B outreach, with an opt-out, are the common case; consumer data and bulk mailing without a basis are not.

## Stop decoding by hand

The same request decodes every protected address on every page you send it, at $0.0002 per page. Your key comes with $2 of free API usage per month.

[Get my free API key](https://app.quanticdata.io/register?utm_source=tool_demo&amp;utm_content=cloudflare-email-decoder) [Read the scrape docs](https://quanticdata.io/docs/#scrape)

$2 of free API usage per month, no credit card.

## Sources and standards

What this tool implements, so you can check it yourself:

- [Cloudflare docs: Email Address Obfuscation](https://developers.cloudflare.com/waf/tools/scrape-shield/email-address-obfuscation/)

- [Cloudflare docs: the /cdn-cgi/ endpoint](https://developers.cloudflare.com/fundamentals/reference/cdn-cgi-endpoint/)

- [MDN: TextDecoder (UTF-8 decoding)](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder)

- [RFC 6068, the mailto URI scheme](https://www.rfc-editor.org/rfc/rfc6068)

## Questions about Cloudflare email protection

### How do I decode [email protected]?

Find the hex behind the placeholder: the data-cfemail attribute of the element, or the part after # in a /cdn-cgi/l/email-protection link. The first byte is the key; XOR every following byte with it and read the result as UTF-8. Paste the hex or the HTML into the decoder above to do it in one step.

### What is /cdn-cgi/l/email-protection?

It is the link target Cloudflare writes in place of a mailto: link when Email Address Obfuscation is on. The encoded address travels after the #. With JavaScript the page's decode script restores the address; without it the link leads to a Cloudflare path with no page behind it.

### Does this decoder send what I paste anywhere?

No. The decoding is JavaScript running on this page, so the hex, links or HTML you paste never leave your browser. Open the Network tab while you use it: decoding makes no requests.

### Why does my scraper get [email protected] instead of the address?

Because Cloudflare decodes the address in the browser with JavaScript, after the HTML arrives. An HTTP client or parser that does not run the page's scripts keeps the placeholder. Decode the data-cfemail hex in your parser, or use a scraping API that decodes it for you.

### How do I turn off Cloudflare email obfuscation on my site?

In the Cloudflare dashboard, open your domain's security settings and switch off Email Address Obfuscation (Scrape Shield). To keep it on but exclude one block, wrap that HTML in the email_off and /email_off comment tags; Cloudflare leaves it untouched.

### Is it legal to collect emails decoded this way?

Decoding is not the legal question; using the data is. Published company addresses for B2B contact are the common case, but in the EU GDPR still applies to personal work addresses, so you need a lawful basis, an opt-out and respect for the site's terms. Consumer data and unsolicited bulk mail are out.

## Related

[Web scraping API Decodes protected emails on every page](https://quanticdata.io/web-scraping-api/) [Email scraper API One contact record per domain](https://quanticdata.io/collectors/email-scraper-api/) [Website to Markdown Any URL as clean Markdown, free](https://quanticdata.io/tools/website-to-markdown/) [Finding business emails Where published addresses live](https://quanticdata.io/blog/how-to-find-business-email-addresses/)

---

Source: https://quanticdata.io/tools/cloudflare-email-decoder/ · Site index for AI: https://quanticdata.io/llms.txt
