Add checks deep-dive section with dangling-records case study
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Introduce a new #checks section presenting domain, zone and service
checkers in three columns linking to the documentation, plus a
dangling-records case study spotlight. Rework discover copy toward
peace-of-mind framing and adjust section backgrounds and CTA layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-06-12 18:24:30 +09:00
commit 4dff3fdaac
11 changed files with 526 additions and 25 deletions

View file

@ -92,7 +92,71 @@
- id: discover-2-tag
translation: "Monitor"
- id: discover-3-tag
translation: "Optional"
translation: "Relax"
- id: checks-eyebrow
translation: "Monitoring"
- id: checks-title
translation: "Deep checks on everything your domain exposes"
- id: checks-lead
translation: "The moment a domain joins happyDomain, our checkers start inspecting it from the outside, exactly as the rest of the world sees it. From the registration down to each live service, every layer is covered."
- id: checks-domain-tag
translation: "Domain"
- id: checks-domain-title
translation: "Your registration"
- id: checks-domain-text
translation: "Expiration date, transfer lock, registrar status: the administrative side of your domain, watched continuously."
- id: checks-zone-tag
translation: "Zone"
- id: checks-zone-title
translation: "Your DNS zone"
- id: checks-zone-text
translation: "DNSSEC validity, delegation consistency, server response: the technical foundation, verified end to end."
- id: checks-services-tag
translation: "Services"
- id: checks-services-title
translation: "Your live services"
- id: checks-services-text
translation: "Websites, certificates, mail: every service your domain points to, probed like a real visitor would."
- id: checks-item-expiration
translation: "Expiration"
- id: checks-item-expiration-detail
translation: "in 23 days"
- id: checks-item-transferlock
translation: "Transfer lock"
- id: checks-item-registration
translation: "Registration status"
- id: checks-item-dnssec
translation: "DNSSEC chain"
- id: checks-item-delegation
translation: "Delegation"
- id: checks-item-responsetime
translation: "Response time"
- id: checks-item-tls
translation: "TLS certificate"
- id: checks-item-http
translation: "HTTP availability"
- id: checks-item-ping
translation: "Ping"
- id: checks-dangling-tag
translation: "Case study"
- id: checks-dangling-title
translation: "Dangling records: the takeover nobody sees coming"
- id: checks-dangling-text
translation: "When a CNAME, MX, NS or SRV record keeps pointing at a resource you no longer control (a decommissioned SaaS, an expired domain, a mistyped name server), anyone can claim that target and publish content, intercept mail or obtain valid certificates under your name."
- id: checks-dangling-text2
translation: |
It is not a theoretical risk. <a href="https://shhaos.github.io/papers/ccs16-dares.pdf" target="_blank">The reference study on the topic</a>, <a href="https://youtube.com/watch?v=2OQ5yysJPlI" target="_blank">presented at ACM CCS 2016</a>, found 467 exploitable dangling records across 277 of the Alexa top 10,000 domains and 52 .edu zones. Since then, <a href="https://arstechnica.com/security/2026/04/why-are-top-university-websites-serving-porn-it-comes-down-to-shoddy-housekeeping/" target="_blank">top universities have been caught serving porn through forgotten CNAMEs (April 2026)</a>, and <a href="https://krebsonsecurity.com/2025/01/mastercard-dns-error-went-unnoticed-for-years/" target="_blank">a typo in one of MasterCard's name servers went unnoticed for nearly five years (January 2025)</a>. happyDomain's dangling-records checker walks every pointer in your zone, resolves its target and verifies its registration, so these forgotten records surface before someone else finds them.
- id: checks-dangling-link
translation: "Read about the dangling-records checker"
- id: checks-dangling-caption
translation: "happyDomain's dangling-records check in action: a forgotten CNAME flagged before anyone can claim it."
- id: checks-footer
translation: "Each check reports a clear status, and you're notified the moment one of them changes."
- id: checks-footer-link
translation: "Browse all checkers in the documentation"
- id: checks-more
translation: "+{{ .Count }} more checkers"
- id: personas-eyebrow
translation: "For everyone"
@ -108,11 +172,11 @@
- id: discover-2-title
translation: "Get instant health checks"
- id: discover-2-text
translation: "From the very first minute, happyDomain runs deep checks on every domain: expiration, DNSSEC, response time and more. Nothing to configure, and you're notified the moment something changes."
translation: "From the very first minute, happyDomain runs deep checks on every domain: expiration, DNSSEC, certificates, response time and more. Nothing to install, nothing to configure."
- id: discover-3-title
translation: "Edit when you want to"
translation: "Enjoy peace of mind"
- id: discover-3-text
translation: "Your provider's console keeps working as before. When you're ready, guided forms, change previews and one-click rollback make every edit safer. It's there to help, never required."
translation: "happyDomain notifies you as soon as a problem affects your domain's configuration or any service discoverable through DNS. And when action is needed, guided editing and one-click rollback are there to help."
- id: discover-assurances-title
translation: "Our promises"

View file

@ -91,7 +91,71 @@
- id: discover-2-tag
translation: "Surveillance"
- id: discover-3-tag
translation: "Optionnel"
translation: "Profitez"
- id: checks-eyebrow
translation: "Surveillance"
- id: checks-title
translation: "Des vérifications approfondies sur tout ce que votre domaine expose"
- id: checks-lead
translation: "Dès qu'un domaine rejoint happyDomain, nos testeurs l'inspectent depuis l'extérieur, exactement comme le reste du monde le voit. De l'enregistrement jusqu'à chaque service en ligne, toutes les couches sont couvertes."
- id: checks-domain-tag
translation: "Domaine"
- id: checks-domain-title
translation: "Votre enregistrement"
- id: checks-domain-text
translation: "Date d'expiration, verrou de transfert, statut auprès du registre : le volet administratif de votre domaine, surveillé en continu."
- id: checks-zone-tag
translation: "Zone"
- id: checks-zone-title
translation: "Votre zone DNS"
- id: checks-zone-text
translation: "Validité DNSSEC, cohérence de la délégation, réponse des serveurs : les fondations techniques, vérifiées de bout en bout."
- id: checks-services-tag
translation: "Services"
- id: checks-services-title
translation: "Vos services en ligne"
- id: checks-services-text
translation: "Sites web, certificats, courriel : chaque service vers lequel pointe votre domaine, sondé comme le ferait un vrai visiteur."
- id: checks-item-expiration
translation: "Expiration"
- id: checks-item-expiration-detail
translation: "dans 23 jours"
- id: checks-item-transferlock
translation: "Verrou de transfert"
- id: checks-item-registration
translation: "Statut d'enregistrement"
- id: checks-item-dnssec
translation: "Chaîne DNSSEC"
- id: checks-item-delegation
translation: "Délégation"
- id: checks-item-responsetime
translation: "Temps de réponse"
- id: checks-item-tls
translation: "Certificat TLS"
- id: checks-item-http
translation: "Disponibilité HTTP"
- id: checks-item-ping
translation: "Ping"
- id: checks-dangling-tag
translation: "Étude de cas"
- id: checks-dangling-title
translation: "Enregistrements orphelins : le détournement que personne ne voit venir"
- id: checks-dangling-text
translation: "Quand un enregistrement CNAME, MX, NS ou SRV continue de pointer vers une ressource que vous ne contrôlez plus (un SaaS décommissionné, un domaine expiré, un serveur de noms mal orthographié), n'importe qui peut récupérer cette cible et publier du contenu, intercepter vos mails ou obtenir des certificats valides en votre nom."
- id: checks-dangling-text2
translation: |
Le risque n'est pas théorique. L'<a href="https://shhaos.github.io/papers/ccs16-dares.pdf" target="_blank">étude de référence sur le sujet</a>, <a href="https://youtube.com/watch?v=2OQ5yysJPlI" target="_blank">présentée à l'ACM CCS 2016</a>, a recensé 467 enregistrements orphelins exploitables dans 277 domaines du top 10 000 Alexa et 52 zones .edu. Depuis, <a href="https://arstechnica.com/security/2026/04/why-are-top-university-websites-serving-porn-it-comes-down-to-shoddy-housekeeping/" target="_blank">de grandes universités ont été surprises à servir du porno via des CNAME oubliés (avril 2026)</a>, et <a href="https://krebsonsecurity.com/2025/01/mastercard-dns-error-went-unnoticed-for-years/" target="_blank">une faute de frappe dans l'un des serveurs de noms de MasterCard est passée inaperçue pendant près de cinq ans (janvier 2025)</a>. Le testeur dangling-records de happyDomain parcourt chaque pointeur de votre zone, résout sa cible et vérifie son enregistrement, pour que ces enregistrements oubliés remontent avant que quelqu'un d'autre ne les trouve.
- id: checks-dangling-link
translation: "Découvrir notre testeur dangling-records"
- id: checks-dangling-caption
translation: "La vérification des enregistrements orphelins de happyDomain en action : un CNAME oublié signalé avant que quelqu'un ne puisse le récupérer."
- id: checks-footer
translation: "Chaque vérification rapporte un statut clair, et vous êtes notifié dès que l'un d'eux change."
- id: checks-footer-link
translation: "Découvrez tous les testeurs dans la documentation"
- id: checks-more
translation: "+{{ .Count }} autres testeurs"
- id: personas-eyebrow
translation: "Pour tout le monde"
@ -105,13 +169,13 @@
- id: discover-1-text
translation: "Reliez votre bureau d'enregistrement, votre hébergeur DNS ou votre serveur faisant autorité en quelques clics. Vos domaines restent exactement là où ils sont : pas de transfert, pas de changement de propriétaire."
- id: discover-2-title
translation: "Profitez de vérifications instantanées"
translation: "Des vérifications immédiates"
- id: discover-2-text
translation: "Dès la première minute, happyDomain effectue des vérifications approfondies sur chaque domaine : expiration, DNSSEC, temps de réponse et bien plus. Rien à configurer, et vous êtes notifié dès qu'un état change."
translation: "Dès la première minute, happyDomain effectue des vérifications approfondies sur chaque domaine : expiration, DNSSEC, certificats, temps de réponse et bien plus. Rien à installer, rien à configurer."
- id: discover-3-title
translation: "Modifiez quand vous le souhaitez"
translation: "Profitez l'esprit tranquille"
- id: discover-3-text
translation: "La console de votre hébergeur continue de fonctionner comme avant. Quand vous êtes prêt, formulaires guidés, aperçu des changements et retour arrière en un clic rendent chaque modification plus sûre. C'est une aide, jamais une obligation."
translation: "happyDomain vous prévient dès qu'un problème touche la configuration de votre domaine ou l'un des services découvrables via le DNS. Et quand il faut agir, l'édition guidée et le retour arrière en un clic sont là pour vous aider."
- id: discover-assurances-title
translation: "Nos promesses"

View file

@ -15,6 +15,8 @@
{{ partial "carousel.html" . }}
{{ partial "checks.html" . }}
{{ partial "features.html" . }}
{{ partial "discover.html" . }}

View file

@ -0,0 +1,136 @@
<section id="checks" class="section">
<div class="container">
<div class="section-head">
<span class="eyebrow"
><span class="dot"></span>{{ i18n "checks-eyebrow" }}</span
>
<h2 class="h2">{{ i18n "checks-title" }}</h2>
<p class="lede">{{ i18n "checks-lead" }}</p>
</div>
{{ $base := printf "https://help.happydomain.org/%s/reference/checkers" .Site.Language.Lang }}
<div class="checks-grid">
<div class="checks-col">
<div class="idea-tag">{{ i18n "checks-domain-tag" }}</div>
<h3>{{ i18n "checks-domain-title" }}</h3>
<p>{{ i18n "checks-domain-text" }}</p>
<ul class="check-list list-unstyled">
<li>
<a href="{{ $base }}/domain-expiry/" target="_blank" rel="noopener" data-umami-event="checks-docs-domain-expiry">
<span class="check-name">{{ i18n "checks-item-expiration" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li>
<a href="{{ $base }}/domain-lock/" target="_blank" rel="noopener" data-umami-event="checks-docs-domain-lock">
<span class="check-name">{{ i18n "checks-item-transferlock" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li>
<a href="{{ $base }}/domain-availability/" target="_blank" rel="noopener" data-umami-event="checks-docs-domain-availability">
<span class="check-name">{{ i18n "checks-item-registration" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li class="check-more">
<a href="{{ $base }}/" target="_blank" rel="noopener" data-umami-event="checks-docs-more-domain">
<span class="check-name">{{ i18n "checks-more" (dict "Count" 3) }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
</ul>
</div>
<div class="checks-col">
<div class="idea-tag">{{ i18n "checks-zone-tag" }}</div>
<h3>{{ i18n "checks-zone-title" }}</h3>
<p>{{ i18n "checks-zone-text" }}</p>
<ul class="check-list list-unstyled">
<li>
<a href="{{ $base }}/dnssec/" target="_blank" rel="noopener" data-umami-event="checks-docs-dnssec">
<span class="check-name">{{ i18n "checks-item-dnssec" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li>
<a href="{{ $base }}/delegation/" target="_blank" rel="noopener" data-umami-event="checks-docs-delegation">
<span class="check-name">{{ i18n "checks-item-delegation" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li>
<a href="{{ $base }}/resolver-propagation/" target="_blank" rel="noopener" data-umami-event="checks-docs-resolver-propagation">
<span class="check-name">{{ i18n "checks-item-responsetime" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li class="check-more">
<a href="{{ $base }}/" target="_blank" rel="noopener" data-umami-event="checks-docs-more-zone">
<span class="check-name">{{ i18n "checks-more" (dict "Count" 10) }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
</ul>
</div>
<div class="checks-col">
<div class="idea-tag">{{ i18n "checks-services-tag" }}</div>
<h3>{{ i18n "checks-services-title" }}</h3>
<p>{{ i18n "checks-services-text" }}</p>
<ul class="check-list list-unstyled">
<li>
<a href="{{ $base }}/tls/" target="_blank" rel="noopener" data-umami-event="checks-docs-tls">
<span class="check-name">{{ i18n "checks-item-tls" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li>
<a href="{{ $base }}/http/" target="_blank" rel="noopener" data-umami-event="checks-docs-http">
<span class="check-name">{{ i18n "checks-item-http" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li>
<a href="{{ $base }}/ping/" target="_blank" rel="noopener" data-umami-event="checks-docs-ping">
<span class="check-name">{{ i18n "checks-item-ping" }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
<li class="check-more">
<a href="{{ $base }}/" target="_blank" rel="noopener" data-umami-event="checks-docs-more-services">
<span class="check-name">{{ i18n "checks-more" (dict "Count" 13) }}</span>
<span class="check-arrow" aria-hidden="true">&rarr;</span>
</a>
</li>
</ul>
</div>
</div>
<div class="checks-spotlight" id="dangling-records" style="scroll-margin-top: 5em">
<div class="spotlight-copy">
<div class="idea-tag idea-tag-danger">{{ i18n "checks-dangling-tag" }}</div>
<h3>{{ i18n "checks-dangling-title" }}</h3>
<p>{{ i18n "checks-dangling-text" }}</p>
<p>{{ i18n "checks-dangling-text2" | safeHTML }}</p>
<a class="spotlight-cta" href="{{ $base }}/dangling-records/" target="_blank" rel="noopener" data-umami-event="checks-docs-dangling">
{{ i18n "checks-dangling-link" }}
<span aria-hidden="true">&rarr;</span>
</a>
</div>
<figure class="spotlight-demo">
<img src="/img/screenshots/dangling-record.webp" alt="{{ i18n "checks-dangling-caption" }}" loading="lazy">
<figcaption>{{ i18n "checks-dangling-caption" }}</figcaption>
</figure>
</div>
<p class="checks-footer">
{{ i18n "checks-footer" }}
<a
href="{{ $base }}/"
target="_blank"
rel="noopener"
data-umami-event="checks-docs"
>{{ i18n "checks-footer-link" }}</a
>
</p>
</div>
</section>

View file

@ -1,24 +1,21 @@
<!-- Call to Action Section -->
<section id="cta">
<div class="container">
<div class="cta-content">
<div class="cta-content" style="text-wrap: balance">
<h2 class="display-5 fw-bold mb-4">
{{ i18n "cta-account-title" }}
</h2>
<div class="row mb-4">
<p
class="offset-sm-1 col-sm-10 offset-md-2 col-md-8 lead text-light"
>
{{ i18n "cta-account-text" }}
</p>
<div class="d-flex justify-content-center mb-4">
<p class="lead text-light">{{ i18n "cta-account-text" }}</p>
</div>
<a
href="/beta/"
href="/{{ .Site.Language.Lang }}/beta/"
class="btn btn-lg btn-light px-4"
style="border-radius: 2em"
data-umami-event="cta-join"
>{{ i18n "cta-account-button" }}</a
>
{{ i18n "cta-account-button" }}
</a>
</div>
</div>
</section>

View file

@ -1,4 +1,4 @@
<section id="discover" class="section section--paper">
<section id="discover" class="section">
<div class="container">
<div class="section-head">
<span class="eyebrow"

View file

@ -1,5 +1,5 @@
<div id="downloads" class="py-5" style="scroll-margin-top: 2em">
<div class="container">
<div id="downloads" class="py-5 my-5" style="scroll-margin-top: 2em">
<div class="container my-5">
<div class="row">
<div
class="col-3 d-none d-md-flex flex-column justify-content-center"
@ -12,14 +12,14 @@
/>
</div>
<div class="col">
<h3 class="fw-bolder">
<h3 class="fw-bolder display-6">
{{ i18n "downloads-title" }}
<span class="text-muted">
{{ i18n "downloads-subtitle" }}
</span>
</h3>
<div
class="row row-cols-1 row-cols-sm-3 my-4 align-items-center"
class="my-5 row row-cols-1 row-cols-sm-3 my-4 align-items-center"
>
<div class="col text-center my-2">
<a

View file

@ -1,4 +1,4 @@
<section id="features" class="section" style="scroll-margin-top: 2em">
<section id="features" class="section section--paper" style="scroll-margin-top: 2em">
<div class="container">
<div class="section-head">
<span class="eyebrow"

View file

@ -1,4 +1,4 @@
<section id="personas" class="section">
<section id="personas" class="section section--paper">
<div class="container">
<div class="section-head">
<span class="eyebrow"

View file

@ -416,6 +416,244 @@ img {
}
}
/* Checks deep-dive (#checks)
Three scope columns sharing one bordered surface, each ending with a
mock check-list whose rows echo the .svc rows of the big-idea section. */
.checks-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
border: 1px solid var(--hd-border-1);
border-radius: 12px;
overflow: hidden;
background: var(--hd-bg-canvas);
}
.checks-col {
display: flex;
flex-direction: column;
padding: 28px 28px 24px;
}
.checks-col + .checks-col {
border-left: 1px solid var(--hd-border-1);
}
.checks-col h3 {
font-weight: 700;
font-size: 1.125rem;
color: var(--hd-fg-1);
letter-spacing: -0.015em;
margin: 0 0 10px;
}
.checks-col > p {
font-size: 0.9375rem;
line-height: 1.6;
color: var(--hd-fg-3);
margin: 0 0 18px;
}
.check-list {
margin: auto 0 0;
border: 1px solid var(--hd-border-1);
border-radius: 8px;
background: var(--hd-bg-subtle);
overflow: hidden;
}
.check-list li + li {
border-top: 1px solid var(--hd-border-1);
}
.check-list li > a {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 9px 12px;
text-decoration: none;
transition: background 0.15s ease, color 0.15s ease;
}
.check-list li > a:hover,
.check-list li > a:focus-visible {
background: var(--hd-accent-subtle);
}
[data-bs-theme="dark"] .check-list li > a:hover,
[data-bs-theme="dark"] .check-list li > a:focus-visible {
background: var(--hd-accent-muted);
}
.check-name {
font-size: 12.5px;
font-weight: 600;
color: var(--hd-fg-2);
}
.check-arrow {
font-size: 13px;
font-weight: 700;
color: var(--hd-fg-4, var(--hd-fg-3));
transform: translateX(-2px);
opacity: 0;
transition: transform 0.15s ease, opacity 0.15s ease;
}
.check-list li > a:hover .check-name,
.check-list li > a:focus-visible .check-name,
.check-list li > a:hover .check-arrow,
.check-list li > a:focus-visible .check-arrow {
color: var(--hd-accent);
}
.check-list li > a:hover .check-arrow,
.check-list li > a:focus-visible .check-arrow {
transform: translateX(0);
opacity: 1;
}
.check-list li.check-more {
background: var(--hd-bg-canvas);
}
.check-list li.check-more .check-name {
color: var(--hd-accent);
font-weight: 700;
}
.check-list li.check-more .check-arrow {
color: var(--hd-accent);
opacity: 1;
transform: translateX(0);
}
/* Dangling-records case study: copy on the left, mock check report on the
right, sharing the same bordered surface language as .checks-grid. */
.checks-spotlight {
display: grid;
grid-template-columns: 1.15fr 1fr;
gap: 36px;
align-items: center;
margin-top: 24px;
padding: 32px;
border: 1px solid var(--hd-border-1);
border-radius: 12px;
background: var(--hd-bg-canvas);
}
.idea-tag-danger {
color: var(--hd-danger);
}
.spotlight-copy h3 {
font-weight: 700;
font-size: 1.25rem;
color: var(--hd-fg-1);
letter-spacing: -0.015em;
margin: 0 0 12px;
}
.spotlight-copy p {
font-size: 0.9375rem;
line-height: 1.6;
color: var(--hd-fg-3);
margin: 0 0 14px;
}
.spotlight-sources-label {
font-weight: 700;
color: var(--hd-fg-2);
margin-bottom: 6px;
}
.spotlight-sources {
margin: 0 0 18px;
}
.spotlight-sources li {
font-size: 0.875rem;
line-height: 1.7;
}
.spotlight-sources a {
color: var(--hd-fg-2);
text-decoration: underline;
text-decoration-color: var(--hd-border-3);
text-underline-offset: 3px;
}
.spotlight-sources a:hover,
.spotlight-sources a:focus-visible {
color: var(--hd-accent);
text-decoration-color: var(--hd-accent);
}
.spotlight-cta {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.9375rem;
font-weight: 600;
color: var(--hd-accent);
text-decoration: none;
}
.spotlight-cta:hover,
.spotlight-cta:focus-visible {
text-decoration: underline;
}
.spotlight-demo {
margin: 0;
}
.spotlight-demo img {
display: block;
width: 100%;
height: auto;
border: 1px solid var(--hd-border-1);
border-radius: 10px;
box-shadow: 0 12px 32px -12px rgba(0, 0, 0, 0.25);
}
.spotlight-demo figcaption {
margin-top: 10px;
font-size: 0.8125rem;
line-height: 1.5;
color: var(--hd-fg-4);
text-align: center;
}
@media (max-width: 991px) {
.checks-spotlight {
grid-template-columns: 1fr;
gap: 24px;
padding: 24px;
}
}
.checks-footer {
margin: 24px 0 0;
font-size: 0.9375rem;
color: var(--hd-fg-3);
}
.checks-footer a {
color: var(--hd-accent);
font-weight: 600;
}
@media (max-width: 767px) {
.checks-grid {
grid-template-columns: 1fr;
}
.checks-col + .checks-col {
border-left: none;
border-top: 1px solid var(--hd-border-1);
}
}
/* ── Personas (#personas) ── */
.personas-grid {
display: grid;

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB