blog / · 4 min read

Hardening a personal domain in an afternoon, for free

DNSSEC, SPF, DKIM, DMARC, MTA-STS, TLS-RPT, CAA, HSTS and getting rid of forgotten subdomains. What each one does, the records I use, and how to check them.

dnsemail-securityhardening

Your personal domain is part of your professional identity. If someone can send mail as you, get a certificate for it or take over a forgotten subdomain, that’s your name on it. I recently cleaned up mine with a free Cloudflare plan and a free mail plan. Here is what each piece does and how to check it. The examples use example.com.

1. Remove what you don’t use

Old subdomains pointing at services you have deleted are the easiest way in. If blog.example.com still has a CNAME to a hosting platform where you closed the account, someone else can claim it and serve content on your domain. This is called subdomain takeover.

List every record, ask “do I still use this?”, and delete the rest. Certificate Transparency logs (crt.sh) show every name you ever got a certificate for, which helps you find the forgotten ones.

2. DNSSEC: signed DNS answers

DNSSEC signs your DNS records, so resolvers that validate can tell when an answer has been forged. Most registrars and DNS providers enable it in one click. Check it:

dig +dnssec example.com SOA | grep -E 'flags:.* ad|RRSIG'

The ad flag means your resolver validated the answer.

3. Email: SPF, DKIM and DMARC

These three work together so that receivers can reject mail that pretends to come from you.

  • SPF lists which servers may send mail for your domain. End it with -all so everything else fails.
  • DKIM signs each message with a key whose public half is in DNS. Use 2048-bit keys.
  • DMARC tells receivers what to do when SPF or DKIM don’t match the visible From address, and where to send reports.
example.com.                    TXT  "v=spf1 include:_spf.mailprovider.example -all"
selector._domainkey.example.com TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."
_dmarc.example.com.             TXT  "v=DMARC1; p=quarantine; sp=reject; adkim=r; aspf=r; fo=1; rua=mailto:[email protected]"

Start DMARC at p=quarantine, read the reports for a couple of weeks, then move to p=reject. sp=reject covers subdomains that never send mail. Reading raw DMARC XML is painful, but Cloudflare’s free DMARC Management turns it into a dashboard.

Check that a real message passes. In Gmail, “Show original” should say:

SPF: PASS   DKIM: 'PASS' with domain example.com   DMARC: 'PASS'

A lesson from my own setup: the provider was still signing with an old, unpublished selector. The DNS looked perfect and the DKIM checks still failed. Always test with a real message.

Domains that never send mail should say so explicitly:

example.net.         TXT "v=spf1 -all"
_dmarc.example.net.  TXT "v=DMARC1; p=reject; sp=reject"
example.net.         MX  0 .

4. MTA-STS and TLS-RPT: encrypted mail delivery

Mail servers encrypt connections opportunistically: an attacker in the middle can strip the encryption and the message goes through in clear. MTA-STS publishes a policy saying “my MX servers always support TLS with valid certificates, refuse to deliver otherwise”. TLS-RPT asks senders to report failures to you.

_mta-sts.example.com.  TXT "v=STSv1; id=20261031"
_smtp._tls.example.com TXT "v=TLSRPTv1; rua=mailto:[email protected]"

The policy itself is a text file served over HTTPS at https://mta-sts.example.com/.well-known/mta-sts.txt:

version: STSv1
mode: testing
mx: mx1.mailprovider.example
mx: mx2.mailprovider.example
max_age: 86400

I serve it from a tiny Cloudflare Worker. Start in testing, watch the TLS-RPT reports, then switch to enforce with a longer max_age. Change the id in the TXT record every time you change the policy.

5. CAA: who may issue certificates for you

A CAA record lists the certificate authorities allowed to issue for your domain. Every public CA must check it before issuing. List only the ones your providers use:

example.com.  CAA 0 issue "letsencrypt.org"
example.com.  CAA 0 issue "pki.goog"
example.com.  CAA 0 issuewild "letsencrypt.org"
example.com.  CAA 0 iodef "mailto:[email protected]"

6. HTTPS done properly

  • Redirect all HTTP to HTTPS.
  • Minimum TLS 1.2, with TLS 1.3 enabled.
  • HSTS, so browsers never try plain HTTP again: max-age=31536000; includeSubDomains. Add preload only once you are sure every subdomain will serve HTTPS forever, because getting off the preload list is slow.
  • Security headers on any site you serve: a strict Content-Security-Policy, X-Content-Type-Options: nosniff, Referrer-Policy and frame-ancestors 'none'.

7. A security.txt

/.well-known/security.txt tells researchers how to reach you (RFC 9116):

Contact: mailto:[email protected]
Expires: 2027-10-31T00:00:00.000Z
Preferred-Languages: en, es
Canonical: https://example.com/.well-known/security.txt

Put a reminder in your calendar before Expires.

What about BIMI?

BIMI shows your logo next to your mail in some inboxes. It needs DMARC at quarantine or reject and, for Gmail and Apple Mail, a paid Verified Mark Certificate tied to a registered trademark. For a personal domain it isn’t worth it.

Check everything in one go

d=example.com
for q in "TXT $d" "TXT _dmarc.$d" "TXT _mta-sts.$d" "TXT _smtp._tls.$d" "CAA $d" "MX $d"; do
  echo "== $q"; dig +short $q
done
curl -s https://mta-sts.$d/.well-known/mta-sts.txt
curl -sI https://$d | grep -i strict-transport

Free external checkers are useful for a second opinion: Hardenize, internet.nl and SSL Labs.

Sources

← All posts