v3: Rebuilt. Advanced content obfuscator for Astro and Starlight.
Supports SSR, SSG, <ClientRouter />, and Trusted Types.
Bots crawl the web for email addresses, phone numbers, and other personal data. This can lead to spam emails, unwanted calls, and other forms of abuse (e.g., invoice fraud).
Most of them take a straightforward approach. However, common obfuscation techniques (e.g., XOR, ROT13) can become predictable once their implementation patterns are known.
Astro Mail Obfuscation minimizes stable patterns that bots can reuse. Each build generates new attribute names, keys, and produces a client runtime obfuscated with JavaScript Obfuscator. Content is AES-GCM-encrypted and ECDSA-signed, while the client deliberately avoids exposing errors.
Without:
<a href="tel:+498999998000">+49 89 99998 000</a>With Astro Mail Obfuscation:
<a data-c8c327ad7b16="IcB8lePUuQlN..."></a>JavaScript restores the content on the client side.
Requires Node.js
>=22.12.0, Astro>=5.0.0 <8.0.0, and a secure context (localhost or HTTPS).
Astro CLI (recommended):
npx astro add astro-mail-obfuscationManual:
npm install astro-mail-obfuscation
# or pnpm add / yarn add / bun add// astro.config.mjs
import { defineConfig } from "astro/config";
import mailObfuscation from "astro-mail-obfuscation";
export default defineConfig({
integrations: [mailObfuscation()],
});Remove all deprecated options from mailObfuscation() in astro.config.mjs. Marker values are no longer evaluated; change data-obfuscation="..." to data-obfuscation.
Add the data-obfuscation marker to any HTML content element:
<a href="mailto:john.doe@example.com" data-obfuscation>
<span>john.doe@example.com</span>
</a>
<p>Tax ID: <span data-obfuscation>[TAX_ID]</span></p>
<address data-obfuscation>
John Doe's Placeholder Emporium<br />
[STREET]<br />
[POSTAL_CODE], [CITY]<br />
[COUNTRY]
</address>All attributes except critical ones (e.g., id, class, style, hidden) are obfuscated.
Do not nest
data-obfuscationor include<noscript>in obfuscated content. Do not obfuscate<astro-island>or its contents, and do not use custom elements as marked roots.
Provide a <noscript> notice for users without JavaScript:
<div>
<a href="mailto:..." data-obfuscation>
<span>...</span>
</a>
<noscript>Please enable JavaScript.</noscript>
</div>Do not put sensitive data inside
<noscript>; use it for a general notice.
To replace the default marker, set markerAttributeName:
// astro.config.mjs
mailObfuscation({
markerAttributeName: "data-shield", // /^data-[a-z0-9_-]+$/
});<a href="mailto:..." data-shield>...</a>stateDiagram-v2
[*] --> Mail_Obfuscation_Server: Setup
Mail_Obfuscation_Server --> Middleware: AES_KEY + ECDSA_PRIVATE_KEY
Middleware --> HTML_Source: Payload (encrypted/signed)
Mail_Obfuscation_Server --> Mail_Obfuscation_Client: AES_KEY + ECDSA_PUBLIC_KEY
Mail_Obfuscation_Vite --> Mail_Obfuscation_Client: obfuscates
HTML_Source --> Mail_Obfuscation_Client
Mail_Obfuscation_Client --> Error: ECDSA:REJECTED
Mail_Obfuscation_Client --> AES_GCM: ECDSA:VERIFIED
AES_GCM --> [*]: restored
The client runtime needs the AES_KEY to decrypt the payload. Because the same key can also be used to create a valid modified payload, AES-GCM alone cannot verify where the payload came from.
ECDSA provides this verification. The client runtime receives only the ECDSA_PUBLIC_KEY; the ECDSA_PRIVATE_KEY remains on the build/server side.
In SSR mode, HTML responses are buffered.
This is required because the integration needs the complete HTML document for validation and transformation. As a result, HTML responses cannot be streamed.
The middleware does not parse or reserialize marker-free HTML, and Cheerio is loaded lazily. All HTML responses are still inspected, and the small client runtime is injected on every page.
Obfuscation is not security. Instead, it raises the cost of automated data extraction.
Astro Mail Obfuscation protects against crawlers and scrapers that inspect raw HTML. It disrupts tools that rely on predictable patterns or static extractors.
Because the integration randomizes its client runtime and regenerates identifiers and cryptographic keys on every build, a single static extractor becomes significantly harder to reuse across sites and builds.
Astro Mail Obfuscation does not sanitize HTML.
Sanitize your content before obfuscation if it comes from an untrusted source (e.g., content management system, user input). For example, use DOMPurify.
If your CSP restricts Trusted Types policy names, configure a static one:
// astro.config.mjs
mailObfuscation({
trustedTypesPolicyName: "tt-a7f3c9", // pass-through only; no sanitization
});Avoid
astro-mail-obfuscationand other obvious names. This option does not modify your CSP headers. Allow the same policy name in yourtrusted-typesdirective.
MIT - (c) 2024-2026 Andreas Brunner.
Built in Germany.