Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0d612ab
fix(public): stop Cloudflare email obfuscation degrading email CTAs t…
nish3451 Aug 12, 2026
5fb6216
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
a1f8dd6
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
54795b5
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
39556fc
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
2d51752
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
c415097
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
3354298
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
3546f34
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
ebc5d2d
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 12, 2026
9391393
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
9f486b1
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
8edaed4
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
8750cae
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
355b4c3
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
f1a4caa
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
7c276ab
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
45ab804
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
1bb36d2
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
2a12230
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
8369284
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
589e458
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 13, 2026
47c6a37
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
086c0c1
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
8fc50b9
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
f5d03e9
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
e25b28f
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
a7d2898
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
132d5c8
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 14, 2026
94e971a
Merge remote-tracking branch 'origin/main' into fix/cloudflare-email-…
nish3451 Aug 18, 2026
0f38445
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 18, 2026
c59ee36
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 19, 2026
5a33dfc
Merge branch 'main' into fix/cloudflare-email-obfuscation-ctas
nish3451 Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions public/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ <h1>This page could not be found.</h1>
<ul class="plain-list">
<li>Check the URL for typos and try again.</li>
<li>Return to the <a href="/">home page</a> and follow a current link.</li>
<li>Email <a href="mailto:support@tinystudio.in">support@tinystudio.in</a> if you need help.</li>
<li>Email <a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a> if you need help.</li>
</ul>
</aside>
</section>
Expand Down Expand Up @@ -114,7 +114,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
</ul>
</div>
<div>
Expand Down
10 changes: 5 additions & 5 deletions public/contact/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ <h1>A direct line to Tiny Studio.</h1>
pages, so every route stays easy to find in one place.
</p>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in">support@tinystudio.in</a>
<a class="button" href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a>
<a class="ghost-button" href="/support/">Support hub</a>
</div>
</div>
Expand Down Expand Up @@ -120,7 +120,7 @@ <h2>The Website Correction</h2>
</p>
<div class="action-row">
<a class="button" href="#website-correction-application">Apply for The Website Correction</a>
<a class="ghost-button" href="mailto:support@tinystudio.in">Plain email to the studio inbox</a>
<a class="ghost-button" href="mailto:support&#64;tinystudio.in">Plain email to the studio inbox</a>
</div>
</div>
</section>
Expand All @@ -140,7 +140,7 @@ <h2 id="website-correction-title">Apply by email, with a visible measurement mar
app. You can remove the marker before sending.
</p>
<div class="action-row">
<a class="button" id="website-correction-apply" href="mailto:support@tinystudio.in?subject=The%20Website%20Correction%20application">Apply to The Website Correction</a>
<a class="button" id="website-correction-apply" href="mailto:support&#64;tinystudio.in?subject=The%20Website%20Correction%20application">Apply to The Website Correction</a>
</div>
</article>
<article>
Expand Down Expand Up @@ -172,7 +172,7 @@ <h2 id="website-correction-title">Apply by email, with a visible measurement mar
<p class="eyebrow">Support</p>
<h2>Primary inbox</h2>
<ul class="plain-list">
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
<li><a href="/support/">tinystudio.in/support/</a></li>
</ul>
</article>
Expand Down Expand Up @@ -284,7 +284,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/drishti/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
<div class="app-status"><span class="dot" aria-hidden="true"></span> Mindful pause before distraction</div>
<h1>Drishti helps bring awareness to screen time before distraction takes over.</h1>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in?subject=Drishti%20early%20access">Get early access</a>
<a class="button" href="mailto:support&#64;tinystudio.in?subject=Drishti%20early%20access">Get early access</a>
<a class="ghost-button" href="/drishti/support/">Drishti support</a>
<a class="ghost-button" href="/drishti/privacy/">Drishti privacy</a>
</div>
Expand Down Expand Up @@ -232,7 +232,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">Drishti email support</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Drishti email support</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/drishti/privacy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ <h2>To respond, improve, and keep records when necessary.</h2>
<article>
<p class="eyebrow">Your choices</p>
<ul class="plain-list">
<li><a href="mailto:support@tinystudio.in">Drishti support email</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Drishti support email</a></li>
<li><a href="/privacy-choices/">Privacy choices and requests</a></li>
</ul>
</article>
Expand Down Expand Up @@ -186,7 +186,7 @@ <h3>Studio</h3>
<ul class="footer-links">
<li><a href="/privacy/">Studio privacy</a></li>
<li><a href="/privacy-choices/">Privacy choices</a></li>
<li><a href="mailto:support@tinystudio.in">Support email</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Support email</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/drishti/support/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ <h1>Drishti support for pauses, permissions, and tracking.</h1>
start.
</p>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in">Email Drishti support</a>
<a class="button" href="mailto:support&#64;tinystudio.in">Email Drishti support</a>
<a class="ghost-button" href="/drishti/privacy/">Drishti privacy</a>
</div>
</div>
Expand Down Expand Up @@ -164,7 +164,7 @@ <h3>Studio</h3>
<ul class="footer-links">
<li><a href="/support/">All support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">Email support</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Email support</a></li>
</ul>
</div>
<div>
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
</ul>
</div>

Expand Down
4 changes: 2 additions & 2 deletions public/privacy-choices/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ <h1>Privacy questions and data requests have a clear route.</h1>
another privacy concern.
</p>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in">Email a privacy request</a>
<a class="button" href="mailto:support&#64;tinystudio.in">Email a privacy request</a>
<a class="ghost-button" href="/privacy/">Studio privacy</a>
</div>
</div>
Expand Down Expand Up @@ -186,7 +186,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">Privacy request email</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Privacy request email</a></li>
</ul>
</div>
<div>
Expand Down
2 changes: 1 addition & 1 deletion public/privacy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/promptly/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
<div class="app-status"><span class="dot" aria-hidden="true"></span> Booking + no-show prevention</div>
<h1>Promptly keeps solo professionals booked, prepared, and harder to ghost.</h1>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in?subject=Promptly%20early%20access">Get early access</a>
<a class="button" href="mailto:support&#64;tinystudio.in?subject=Promptly%20early%20access">Get early access</a>
<a class="ghost-button" href="/promptly/support/">Promptly support</a>
<a class="ghost-button" href="/promptly/privacy/">Promptly privacy</a>
</div>
Expand Down Expand Up @@ -233,7 +233,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">Promptly email support</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Promptly email support</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/promptly/privacy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ <h2>Booking links, notes, payment proof, and support records.</h2>
<article>
<p class="eyebrow">Your choices</p>
<ul class="plain-list">
<li><a href="mailto:support@tinystudio.in">Promptly support email</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Promptly support email</a></li>
<li><a href="/privacy-choices/">Privacy choices and requests</a></li>
<li>Calendar and notification access can be changed in device settings.</li>
<li>Clients can use signed booking links without installing the app.</li>
Expand Down Expand Up @@ -214,7 +214,7 @@ <h3>Studio</h3>
<ul class="footer-links">
<li><a href="/privacy/">Studio privacy</a></li>
<li><a href="/privacy-choices/">Privacy choices</a></li>
<li><a href="mailto:support@tinystudio.in">Support email</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Support email</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/promptly/support/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ <h1>Promptly support for bookings, reminders, and client links.</h1>
links, this is where to start.
</p>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in">Email Promptly support</a>
<a class="button" href="mailto:support&#64;tinystudio.in">Email Promptly support</a>
<a class="ghost-button" href="/promptly/privacy/">Promptly privacy</a>
</div>
</div>
Expand Down Expand Up @@ -165,7 +165,7 @@ <h3>Studio</h3>
<ul class="footer-links">
<li><a href="/support/">All support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">Email support</a></li>
<li><a href="mailto:support&#64;tinystudio.in">Email support</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/support/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ <h1>Support that stays simple and easy to find.</h1>
Promptly, Drishti, or the studio itself.
</p>
<div class="action-row">
<a class="button" href="mailto:support@tinystudio.in">Email support@tinystudio.in</a>
<a class="button" href="mailto:support&#64;tinystudio.in">Email support&#64;tinystudio.in</a>
<a class="ghost-button" href="/contact/">Studio contact</a>
</div>
<div class="meta-row">
Expand Down Expand Up @@ -195,7 +195,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
</ul>
</div>
<div>
Expand Down
4 changes: 2 additions & 2 deletions public/terms/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ <h2>Use the public contact routes.</h2>
<article>
<p class="eyebrow">Public email</p>
<ul class="plain-list">
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
<li><a href="/privacy-choices/">Privacy choices</a></li>
</ul>
</article>
Expand Down Expand Up @@ -179,7 +179,7 @@ <h3>Support</h3>
<ul class="footer-links">
<li><a href="/support/">Support</a></li>
<li><a href="/contact/">Contact</a></li>
<li><a href="mailto:support@tinystudio.in">support@tinystudio.in</a></li>
<li><a href="mailto:support&#64;tinystudio.in">support&#64;tinystudio.in</a></li>
</ul>
</div>
<div>
Expand Down
10 changes: 10 additions & 0 deletions scripts/check-public-live-deploy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ try {
"the entire managed-service section block is absent from the homepage"
)
}

console.log("E. no Cloudflare email-obfuscation placeholder on the live homepage")
{
const { status, body } = await get("/")
ok(status === 200, `homepage returns 200 (got ${status})`)
ok(body.includes("support&#64;tinystudio.in"), "homepage serves the email entity-encoded (browser-decoded, edge-immutable)")
ok(!body.includes("__cf_email__"), "no Cloudflare-obfuscated email span on the homepage")
ok(!body.includes("[email"), "no '[email protected]' placeholder text on the homepage")
ok(!body.includes("support@tinystudio.in"), "no plaintext email left that Email Address Obfuscation could rewrite")
}
} catch (error) {
failures++
console.error(` FAIL live request error: ${error.message}`)
Expand Down
9 changes: 7 additions & 2 deletions scripts/prepare-static-site-bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,15 @@ function removeGeneratedHeadAssets(html) {
.replace(/\n\s*<script data-cfasync="false" src="\/cdn-cgi\/scripts\/5c5dd728\/cloudflare-static\/email-decode\.min\.js"><\/script>/g, "");
}

// Restore Cloudflare email-obfuscation artifacts (the __cf_email__ span and
// the /cdn-cgi/l/email-protection href) to the canonical, obfuscation-proof
// form: the @ is entity-encoded (support&#64;tinystudio.in) so the zone-level
// Email Address Obfuscation rewrite at the edge has no literal @ to match and
// the email CTA can never degrade to the "[email protected]" placeholder.
function replaceCloudflareEmailProtection(html) {
return html
.replace(/href="\/cdn-cgi\/l\/email-protection#[^"]+"/g, 'href="mailto:support@tinystudio.in"')
.replace(/<span class="__cf_email__" data-cfemail="[^"]+">\[email&#160;protected\]<\/span>/g, "support@tinystudio.in");
.replace(/href="\/cdn-cgi\/l\/email-protection#[^"]+"/g, 'href="mailto:support&#64;tinystudio.in"')
.replace(/<span class="__cf_email__" data-cfemail="[^"]+">\[email&#160;protected\]<\/span>/g, "support&#64;tinystudio.in");
}

function fixSupportHeadingOrder(html) {
Expand Down
7 changes: 6 additions & 1 deletion scripts/test-public-conversion-signal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,13 @@ for (const a of ctaTags) {

console.log("D. contact endpoint source propagation")
const contactHtml = read(CONTACT_HTML)
// The @ is entity-encoded in served HTML (support&#64;tinystudio.in) so the
// zone-level Cloudflare Email Address Obfuscation rewrite cannot degrade the
// CTA to the "[email protected]" placeholder; browsers decode the entity, so
// the mailto route below is inspected on a decoded copy.
const contactHtmlDecodedAts = contactHtml.replace(/&#64;/gi, "@")
ok(
/href\s*=\s*"mailto:support@tinystudio\.in\?subject=[^"]*"/i.test(contactHtml),
/href\s*=\s*"mailto:support@tinystudio\.in\?subject=[^"]*"/i.test(contactHtmlDecodedAts),
"contact page has a Website Correction application mailto route"
)
ok(
Expand Down
31 changes: 30 additions & 1 deletion scripts/test-public-deploy-bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const filteredContact = readFileSync(join(bundleDir, "contact/index.html"), "utf
ok(filteredContact.includes("<html") && filteredContact.includes("</html>"), "contact is still a complete html document")
ok(filteredContact.includes("Primary inbox"), "neutral support card remains")
ok(filteredContact.includes("App-specific public pages"), "neutral apps card remains")
ok(filteredContact.includes("mailto:support@tinystudio.in"), "studio inbox mailto remains")
ok(filteredContact.includes("mailto:support&#64;tinystudio.in"), "studio inbox mailto remains (entity-encoded, obfuscation-proof)")
ok(!filteredContact.includes("<script>"), "the measurement-marker script is gone with the application section")

console.log("G. npm test/ci wiring")
Expand All @@ -134,6 +134,35 @@ ok(pkg.scripts.test.includes("test-public-deploy-bundle.mjs"), "npm test runs th
ok(pkg.scripts.ci.includes("test-public-deploy-bundle.mjs"), "npm run ci runs the deploy-bundle test")
ok(pkg.scripts["site:publish"], "site:publish npm script exists")

console.log("H. no plaintext email is served that Cloudflare Email Address Obfuscation can rewrite")
// The tinystudio.in zone rewrites literal user@host emails in served HTML into
// __cf_email__ spans that render as the "[email protected]" placeholder when
// the decode script does not run. The email must therefore stay entity-encoded
// (support&#64;tinystudio.in) in every served file; browsers decode the entity
// in text and hrefs, Cloudflare's obfuscation regex has no literal @ to match.
const PUBLIC_HTML = [
"index.html",
"404.html",
"support/index.html",
"contact/index.html",
"privacy/index.html",
"privacy-choices/index.html",
"terms/index.html",
"promptly/index.html",
"promptly/support/index.html",
"promptly/privacy/index.html",
"drishti/index.html",
"drishti/support/index.html",
"drishti/privacy/index.html",
]
const scriptAwareParts = (html) => html.split(/<script\b[^>]*>[\s\S]*?<\/script>/)
const htmlContent = (html) => scriptAwareParts(html).join("\n")
for (const rel of PUBLIC_HTML) {
const served = htmlContent(read(`public/${rel}`))
ok(!served.includes("support@tinystudio.in"), `${rel} has no plaintext email outside script blocks`)
ok(served.includes("support&#64;tinystudio.in"), `${rel} serves the email entity-encoded`)
}

rmSync(bundleDir, { recursive: true, force: true })

console.log(`\n${checks} checks, ${failures} failures`)
Expand Down