Anyone can send an email that claims to come from your domain. The SMTP protocol has no built-in check on the sender address, so attackers use this to run phishing campaigns, impersonate executives, and damage your reputation with recipients. Three DNS-based standards fix most of this: SPF lists which servers may send mail for your domain, DKIM attaches a cryptographic signature to outgoing messages, and DMARC tells receiving servers what to do when a message fails those checks and where to send you reports.

This guide is for sysadmins and self-hosters who run their own domain and mail server. You will publish all three records, verify them with real tools, and move DMARC from a monitoring-only policy to full enforcement. The commands use placeholders such as YOUR_DOMAIN, which you should replace with your own values.

Before you start

  • Administrative access to the DNS zone for your domain, or to the DNS provider’s control panel.
  • A complete list of every service that sends mail as your domain: your mail server, marketing platforms, ticketing systems, invoicing tools, and anything that relays through a smarthost.
  • Shell access to the mail server that sends your outbound mail, with the ability to install and configure a DKIM signer (OpenDKIM is used in the examples below, but rspamd or your provider’s signing feature works too).
  • OpenSSL and the dig utility installed on your workstation. On Debian and Ubuntu, dig comes from the dnsutils package. On RHEL-family systems, it comes from bind-utils.
  • A mailbox you control that will receive DMARC aggregate reports, preferably one dedicated to that purpose.
  • Test accounts at a couple of large mailbox providers so you can inspect real message headers.

Step 1: Inventory your legitimate senders

Before you publish any record, write down every system that sends mail using your domain in the From address. Missing one is the most common reason legitimate mail starts failing after a DMARC policy change. Check your mail server logs, your application configuration files, and the settings pages of SaaS tools for any that offer custom sending domains.

For each sender, note whether it can sign with DKIM using a key you control, and whether it sends from a fixed set of IP addresses. You will need both facts for the next steps.

Step 2: Publish an SPF record

SPF is a single TXT record at the root of your domain. It lists the hosts and services allowed to send mail for your domain. A receiving server looks up the record, checks the connecting IP address against it, and records a pass or fail.

Start with a record that describes only what you actually use. Here is a typical example where your own mail server and one third-party provider send mail:

YOUR_DOMAIN.  3600  IN  TXT  "v=spf1 mx ip4:203.0.113.10 include:YOUR_MAIL_PROVIDER -all"

The mechanisms mean the following:

  • mx authorizes the hosts listed in your MX records.
  • ip4: authorizes a specific address. Use ip6: for IPv6 addresses.
  • include: delegates authorization to another domain’s SPF record, which is how you authorize a third-party sender. Use the hostname your provider documents.
  • -all says that any other sender is a hard fail. Start with ~all (softfail) during the initial rollout if you are unsure about your sender list, then change it to -all once you have verified everything.

Keep these rules in mind. A domain must have only one SPF record; publishing two causes both to be treated as invalid. SPF evaluation allows a maximum of 10 DNS lookups, and each include, mx, and a mechanism counts toward that limit. Avoid unnecessary ptr mechanisms, which are deprecated.

Step 3: Generate a DKIM key pair and publish the public key

DKIM uses a private key on your sending server to sign each message, and a public key published in DNS so receivers can verify the signature. Create a 2048-bit RSA key pair with a selector name. The selector lets you rotate keys later without downtime. In this example the selector is mail.

sudo mkdir -p /etc/opendkim/keys/YOUR_DOMAIN
cd /etc/opendkim/keys/YOUR_DOMAIN
sudo openssl genrsa -out mail.private 2048
sudo openssl rsa -in mail.private -pubout -outform der 2>/dev/null | openssl base64 -A > mail.pubkey.b64
sudo chmod 600 mail.private

The file mail.pubkey.b64 now contains the public key as a single base64 string. Publish it as a TXT record at the selector name followed by ._domainkey:

mail._domainkey.YOUR_DOMAIN.  3600  IN  TXT  "v=DKIM1; k=rsa; p=PASTE_CONTENTS_OF_mail.pubkey.b64_HERE"

Most DNS providers accept a long TXT value in one piece. If yours requires splitting, divide the value into quoted strings of no more than 255 characters each, and let the resolver join them. Do not insert spaces or line breaks inside the key itself.

Note that the > characters above are shown as HTML entities. In your shell, use the plain greater-than character.

Step 4: Configure your mail server to sign outbound mail

Publishing a key does nothing until your server signs messages with it. On a Debian or Ubuntu system running Postfix, install OpenDKIM and point it at the key you created. Edit /etc/opendkim.conf so it contains these directives:

Domain          YOUR_DOMAIN
Selector        mail
KeyFile         /etc/opendkim/keys/YOUR_DOMAIN/mail.private
Socket          inet:8891@localhost
UMask           002
Canonicalization relaxed/simple
Mode            sv

Next, tell Postfix to send outbound and local mail through the OpenDKIM milter. Add these lines to /etc/postfix/main.cf:

milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Restart both services and check that they are running:

sudo systemctl restart opendkim postfix
sudo systemctl status opendkim --no-pager

If you send mail through a third-party provider, configure DKIM signing in that provider’s dashboard instead. Most providers generate the key pair for you and give you a selector name and a TXT value to publish. Use their values in place of the ones above.

Step 5: Publish a DMARC policy in monitoring mode

DMARC lives at the _dmarc label. It tells receivers how to handle mail that fails alignment, meaning the SPF or DKIM result must match the domain in the visible From address. It also specifies where to send aggregate reports. Begin with p=none, which collects data without changing how mail is delivered:

_dmarc.YOUR_DOMAIN.  3600  IN  TXT  "v=DMARC1; p=none; rua=mailto:dmarc-reports@YOUR_DOMAIN; adkim=s; aspf=s; pct=100"

The tags do the following. p sets the policy for the domain. rua names the mailbox for aggregate reports. adkim and aspf set DKIM and SPF alignment to strict (s) or relaxed (r). Strict alignment requires an exact domain match, which is appropriate if all your mail comes from your own domain. pct sets the percentage of failing mail the policy applies to, and only matters once the policy is quarantine or reject.

If your reports go to a mailbox on a different domain, that domain must publish an authorization record so receivers accept the reports. Publish this at the reporting domain, using the name YOUR_DOMAIN._report._dmarc.REPORTING_DOMAIN, with the value v=DMARC1. Sending reports to a mailbox on the same domain avoids this step.

Step 6: Verify each record

Check that DNS is serving the records you published. Query each name directly and compare the output with what you intended. Propagation can take as long as the record TTL, so wait for that interval if you see stale answers.

dig +short TXT YOUR_DOMAIN
dig +short TXT mail._domainkey.YOUR_DOMAIN
dig +short TXT _dmarc.YOUR_DOMAIN

The SPF query should return exactly one string beginning with v=spf1. The DKIM query should return the v=DKIM1 string with your public key. The DMARC query should return the v=DMARC1 policy.

Next, test the signing key itself. OpenDKIM ships a test tool that fetches the public key from DNS and compares it with the private key:

sudo opendkim-testkey -d YOUR_DOMAIN -s mail -vvv

A successful run ends with a line reporting that the key OK. If it reports a missing record or a key mismatch, recheck the TXT value for stray spaces, truncated characters, or a copy from the wrong file.

Finally, send a real message. Send one from your domain to an account at a major mailbox provider, open the message, and view its original headers. Look for an Authentication-Results header that shows all three results as passing:

Authentication-Results: mx.example.net;
 dkim=pass header.d=YOUR_DOMAIN header.s=mail;
 spf=pass smtp.mailfrom=YOUR_DOMAIN;
 dmarc=pass header.from=YOUR_DOMAIN

If SPF passes but DKIM fails, your signer is probably not running or is signing a different domain than the From address. If DKIM passes but DMARC fails, the signing domain or the SPF envelope sender does not align with the From domain.

Step 7: Read the reports and move toward enforcement

Aggregate reports arrive as XML attachments, usually once a day per reporting organization. They list the sending IP addresses that claimed to be your domain, how many messages each sent, and whether SPF and DKIM passed. Many free and commercial tools parse these reports into readable summaries, so you do not have to read the XML by hand.

Over one to four weeks, use the reports to answer two questions. First, does every legitimate sender appear with passing results? Second, what unknown sources are sending as your domain? Fix legitimate senders by adding their SPF entries or enabling DKIM signing for them. Unknown sources are usually spoofing attempts, which is exactly what enforcement will block.

When legitimate mail passes consistently, tighten the policy in stages. Change the record to p=quarantine so failing mail goes to spam folders, and watch the reports and any user complaints for a week. Then change it to p=reject:

_dmarc.YOUR_DOMAIN.  3600  IN  TXT  "v=DMARC1; p=reject; rua=mailto:dmarc-reports@YOUR_DOMAIN; adkim=s; aspf=s; pct=100"

Also protect domains that never send mail. Any domain or subdomain that should not send email should publish a null SPF record and a reject DMARC policy, so spoofed messages claiming to come from it fail outright:

YOUR_DOMAIN.           3600  IN  TXT  "v=spf1 -all"
_dmarc.YOUR_DOMAIN.    3600  IN  TXT  "v=DMARC1; p=reject; sp=reject"

The sp tag sets the policy for subdomains that do not have their own DMARC record.

Verify it worked

  • All three queries in Step 6 return the expected records, and the DKIM test tool reports a key match.
  • A test message to a major mailbox provider shows spf=pass, dkim=pass, and dmarc=pass in its authentication headers.
  • Your DMARC reports show every legitimate sender passing alignment for several consecutive reporting periods.
  • A test message sent from a spoofed address (for example, from a script that sets a different From header) is quarantined or rejected by a receiving server, and the attempt appears in your reports.
  • Your non-mail domains return the null SPF record and a reject DMARC policy.

Common mistakes

  • Publishing two SPF records. Merge every mechanism into a single v=spf1 string. Duplicate records cause a permanent SPF error.
  • Exceeding 10 SPF lookups. Each include can pull in more includes. Use your provider’s flattened or current include values, and remove mechanisms for services you no longer use.
  • Jumping straight to reject. Skipping the monitoring stage means discovering your forgotten invoicing system or newsletter tool when customers stop receiving legitimate mail.
  • Breaking forwarding. Forwarding services change the envelope sender, so SPF often fails after a forward. DKIM signatures usually survive forwarding, which is one reason DKIM alignment matters. Mailing lists that rewrite the subject or body can break DKIM too, so watch for these in your reports.
  • Mangling the DKIM key. Copying a key with extra spaces, a missing character, or a line break inside the value causes a silent failure. Use opendkim-testkey after every change.
  • Using the wrong selector. The name in the DNS record must exactly match the Selector value in the signer’s configuration.
  • Forgetting subdomains. Subdomains used for sending, such as a support or marketing subdomain, need their own SPF and DKIM setup or an explicit DMARC policy.
  • Ignoring the reports. A DMARC record with no one reading its reports gives you no protection beyond the policy itself. Set up a mailbox and a summary process before you tighten the policy.
  • Lowering TTLs too late. During rollout, use a short TTL such as 300 seconds so corrections take effect quickly. Raise it to a longer value once the records are stable.

Takeaway

SPF says which servers may send for your domain, DKIM proves that a message was signed by your domain, and DMARC tells receivers what to do when those checks fail and sends you the evidence. Build the inventory first, publish each record, verify it with real headers and reports, and only then move to enforcement. Once p=reject is in place and monitored, spoofed mail using your domain stops reaching your recipients, and you get a steady feed of data showing who is trying.