> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# DNS Mail DKIM

> Probes common DKIM selectors and validates the published signing keys so receivers can verify that email from the domain is authentic.

<Info>
  **Security Auditor** — looks up the DKIM key records of the audited domain at 26 common selectors (`<selector>._domainkey.<domain>`) and validates every key it finds against RFC 6376, RFC 8301 and RFC 8463.
</Info>

<CardGroup cols={2}>
  <Card title="Protection" icon="shield-halved">
    A valid DKIM key lets receivers prove that mail signed by your domain was not forged or altered in transit.
  </Card>

  <Card title="Standards" icon="book">
    Follows RFC 6376 (DKIM), RFC 8301 (RSA key sizes) and RFC 8463 (Ed25519 keys).
  </Card>
</CardGroup>

DKIM lets a mail server sign outgoing messages with a private key; receivers fetch the matching public key from DNS at `<selector>._domainkey.<domain>` and verify the signature. A missing, revoked, malformed or weak key means the signature cannot be verified, so legitimate mail loses its DKIM credit (and often lands in spam) while forged mail is no harder to send. The selector is only known from a signed message, so this auditor guesses the selectors used by the major providers.

## How it works

1. **Domain** — takes the host of the audited URL and strips one leading `www.`. A URL without a host ends the audit with `501`. The audited page is never fetched.
2. **Probe** — queries the TXT record of `<selector>._domainkey.<domain>` for each of the 26 common selectors: `default, dkim, google, google1, google2, k1, key1, key2, mandrill, mg, mta1, mta2, pm, protonmail, protonmail2, resend, s1, s2, scph, selector1, selector2, sendgrid, smtp, zoho, zoho1, zoho2`. When every lookup fails at the resolver the audit ends with `500`; when only some fail, the rest are audited and the failures are listed in the check data.
3. **Parse** — each record found is parsed once: the tags in record order (the character strings of a split TXT record are joined first), the key type (`k`, default `rsa`), the base64 `p` tag and, for a known key type, the key itself (RSA modulus size from the SubjectPublicKeyInfo, or the raw 32-byte Ed25519 point).
4. **Rules** — per selector: a `v` tag that is not `DKIM1` (`400`), `v=DKIM1` not first (`401`), an unknown `k` (`402`), no `p` tag (`403`), an empty `p` tag (`404`), a `p` tag that is not base64 (`405`), an RSA key under 1024 bits (`406`), an Ed25519 key that is not 32 bytes (`407`) or not a curve point (`408`), an RSA-typed key that is not a SubjectPublicKeyInfo (`409`); a missing `v` tag (`300`), an RSA key of exactly 1024 bits (`301`) and a key of another algorithm (`302`) warn. When no selector answers at all the audit warns (`303`), because a selector outside the list cannot be seen.
5. **Aggregate** — FAIL wins over WARNING. Every selector that passed all rules gets its own `200` check, even when another selector failed; the audit is `SUCCESS` only when nothing fired.
6. **Specification pin** — every run emits `37A-DnsMailDkimAuditor-100` (`INFORMATION`), naming [RFC 6376](https://www.rfc-editor.org/rfc/rfc6376) (September 2011) with RFC 8301 and RFC 8463 as notes. It does not change the overall status.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`, in the order the auditor runs them. Per-selector rules emit one check per offending selector; `{selector}`, `{fqdn}` and `{record}` are the selector name, `<selector>._domainkey.<domain>` and the TXT record text. CheckCode suffixes use status ranges: `100` information, `200` success, `300–399` warning, `400–499` fail, `500–599` error.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-DnsMailDkimAuditor-100" />`37A-DnsMailDkimAuditor-100` | `INFORMATION` | Always. Records the specification this auditor implements (RFC 6376, with RFC 8301 and RFC 8463 as notes). | — (recommendation is `null`; nothing for the site to change) | — |
| <a id="37A-DnsMailDkimAuditor-303" />`37A-DnsMailDkimAuditor-303` | `WARNING` | None of the 26 probed selectors has a TXT record. | Publish the DKIM key of your email provider as a TXT record at `<selector>._domainkey.{domain}`; if `{domain}` already signs mail with a selector outside this list (`{selectors}`), the key is valid but cannot be checked here. | Without a DKIM key receivers cannot verify that mail claiming to come from your domain is genuine, so legitimate messages land in spam and forged ones pass as yours. |
| <a id="37A-DnsMailDkimAuditor-400" />`37A-DnsMailDkimAuditor-400` | `FAIL` | The record has a `v` tag whose value is not `DKIM1` (case-insensitive). | Change the v tag of the TXT record at `{fqdn}` from "`{v}`" to "DKIM1". | Receivers discard a key record whose version is not DKIM1, so every signature made with this selector fails and the mail loses its DKIM protection. |
| <a id="37A-DnsMailDkimAuditor-401" />`37A-DnsMailDkimAuditor-401` | `FAIL` | `v=DKIM1` is present but another tag comes first. | Move v=DKIM1 to the start of the TXT record at `{fqdn}`, before "`{firstTag}`". | Receivers that enforce the tag order treat the record as invalid, so mail signed with this selector fails verification at them. |
| <a id="37A-DnsMailDkimAuditor-402" />`37A-DnsMailDkimAuditor-402` | `FAIL` | The `k` tag is present and neither `rsa` nor `ed25519`. | Set k=rsa or k=ed25519 (matching the published key) in the TXT record at `{fqdn}` instead of "`{k}`", or remove the k tag to default to rsa. | Receivers do not know how to use a key of an undefined type, so every signature made with this selector fails verification. |
| <a id="37A-DnsMailDkimAuditor-403" />`37A-DnsMailDkimAuditor-403` | `FAIL` | The record has no `p` tag. The key rules are skipped for that selector. | Add the p tag with the base64 public key of the signing key to the TXT record at `{fqdn}`. | Without a public key receivers cannot verify any signature made with this selector, so the mail is treated as unsigned. |
| <a id="37A-DnsMailDkimAuditor-404" />`37A-DnsMailDkimAuditor-404` | `FAIL` | The `p` tag is present but empty, which RFC 6376 section 3.6.1 defines as a revoked key. The key rules are skipped. | Publish the current public key in the p tag at `{fqdn}`, or remove the record if selector "`{selector}`" is retired. | A revoked key means every message still signed with this selector fails DKIM, so receivers treat it as unsigned or spoofed. |
| <a id="37A-DnsMailDkimAuditor-405" />`37A-DnsMailDkimAuditor-405` | `FAIL` | The `p` tag is not valid base64. The key rules are skipped. | Replace the p tag at `{fqdn}` with the base64 public key exported from the signer, without truncation or extra characters; the current value is "`{p}`". | Receivers cannot decode the key, so every signature made with this selector fails verification. |
| <a id="37A-DnsMailDkimAuditor-300" />`37A-DnsMailDkimAuditor-300` | `WARNING` | The record has no `v` tag. | Add v=DKIM1 as the first tag of the TXT record at `{fqdn}`, before "`{firstTag}`". | Receivers assume DKIM1 when the tag is absent, but an explicit version keeps the record from being misread once newer DKIM versions appear. |
| <a id="37A-DnsMailDkimAuditor-406" />`37A-DnsMailDkimAuditor-406` | `FAIL` | The RSA key is shorter than 1024 bits. | Generate a 2048-bit RSA key for selector "`{selector}`" and publish it at `{fqdn}`; the current key is `{rsaBits}` bits. | Receivers must reject signatures from RSA keys under 1024 bits, so the mail fails DKIM, and such keys are cheap to factor and forge. |
| <a id="37A-DnsMailDkimAuditor-407" />`37A-DnsMailDkimAuditor-407` | `FAIL` | `k=ed25519` and the decoded key is not 32 bytes. | Publish the raw 32-byte Ed25519 public key (base64, not the SubjectPublicKeyInfo wrapper) in the p tag at `{fqdn}`; the current value decodes to `{bytes}` bytes. | A key of the wrong length cannot be used by receivers, so every signature made with this selector fails verification. |
| <a id="37A-DnsMailDkimAuditor-408" />`37A-DnsMailDkimAuditor-408` | `FAIL` | `k=ed25519`, the key is 32 bytes, but it is not a valid point on the curve. | Publish the Ed25519 public key exactly as exported by the signer in the p tag at `{fqdn}`; the current value "`{p}`" is not a point on the curve. | Receivers reject a key that is not on the curve, so every signature made with this selector fails verification. |
| <a id="37A-DnsMailDkimAuditor-409" />`37A-DnsMailDkimAuditor-409` | `FAIL` | `k=rsa` (or no `k`) and the decoded key is not a SubjectPublicKeyInfo structure. | Publish the full DER-encoded public key (as printed by "openssl rsa -pubout", without the PEM header lines) in the p tag at `{fqdn}`, and check that the TXT record was not truncated. | Receivers cannot read the key, so every signature made with this selector fails verification and the mail loses its DKIM protection. |
| <a id="37A-DnsMailDkimAuditor-301" />`37A-DnsMailDkimAuditor-301` | `WARNING` | The RSA key is exactly 1024 bits. | Rotate selector "`{selector}`" to a 2048-bit RSA key and publish it at `{fqdn}`; the current key is 1024 bits. | A 1024-bit RSA key is within reach of a well-funded attacker and some receivers already distrust it, so your mail can lose its DKIM credit or be forged. |
| <a id="37A-DnsMailDkimAuditor-302" />`37A-DnsMailDkimAuditor-302` | `WARNING` | The key parses as SubjectPublicKeyInfo but its algorithm is neither RSA nor Ed25519. | Publish an RSA (2048-bit) or Ed25519 key at `{fqdn}` instead of the `{oid}` key. | Receivers only verify RSA and Ed25519 DKIM signatures, so a key of another algorithm leaves your mail unverifiable at most of them. |
| <a id="37A-DnsMailDkimAuditor-200" />`37A-DnsMailDkimAuditor-200` | `SUCCESS` | One check per selector whose record passed every rule. Message names the selector, its FQDN, the key type (RSA with its size, or Ed25519) and the record. | — | — |
| <a id="37A-DnsMailDkimAuditor-500" />`37A-DnsMailDkimAuditor-500` | `ERROR` | Every one of the 26 selector lookups failed at the resolver (SERVFAIL, timeout), so no selector could be checked. | Check that the name servers of `{domain}` answer TXT queries for `*._domainkey.{domain}`, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-DnsMailDkimAuditor-501" />`37A-DnsMailDkimAuditor-501` | `ERROR` | The audited URL is empty, not a valid URI, or has no host to derive a domain from. | Run the audit with an absolute http(s) URL such as [https://example.com](https://example.com) instead of "`{url}`". | — |

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | Every DKIM key found is valid | At least one of the 26 selectors has a record and every record passed all rules; one `200` check per valid selector. |
| **WARNING** | A key should be improved, or no selector was found | No `v` tag (`300`); RSA key of exactly 1024 bits (`301`); a key of an algorithm other than RSA or Ed25519 (`302`); no record at any probed selector (`303`). |
| **FAIL** | A key cannot be used by receivers | Wrong version (`400`); version not first (`401`); unknown key type (`402`); no `p` tag (`403`); revoked key (`404`); invalid base64 (`405`); RSA under 1024 bits (`406`); Ed25519 key not 32 bytes (`407`) or not on the curve (`408`); RSA-typed key that is not a SubjectPublicKeyInfo (`409`). |
| **ERROR** | The auditor could not check the domain | Every selector lookup failed at the resolver (`500`); the URL has no usable host (`501`). |
| **INFORMATION** | Specification pin | Always emitted (`100`); does not change overall status. |

## Risks and considerations

### Selector guessing

* DKIM selectors are private to the signer; only a signed message reveals them. The auditor probes 26 common ones, so a domain that signs with a custom selector shows `303` although its DKIM works. That is why `303` is a warning and not a failure.
* Only the first TXT value of a selector is audited; RFC 6376 leaves several records at one name undefined.
* A resolver failure on some selectors does not stop the audit: those selectors are listed in `lookupErrors` in the check data. Only a failure on every selector ends the audit with `500`.

### Key strength

* RFC 8301 forbids receivers from accepting RSA keys under 1024 bits (`406`) and recommends 2048 bits; a 1024-bit key still verifies but is worth rotating (`301`).
* RFC 8463 Ed25519 keys are published raw (32 bytes). A SubjectPublicKeyInfo-wrapped Ed25519 key under `k=rsa` is accepted as valid; under `k=ed25519` it is 44 bytes and fails `407`.

### Changes from the previous version of this auditor

* No selector found was `FAIL`; it is now `WARNING` (`303`), because selector guessing cannot prove that DKIM is absent.
* An RSA key under 1024 bits was `WARNING`; it is now `FAIL` (`406`), as RFC 8301 forbids receivers from using it.
* A key that does not parse as SubjectPublicKeyInfo was `ERROR`; it is now `FAIL` (`409`): bad published data is a site problem, not an auditor failure.
* A missing or empty `p` tag used to add a second "not valid base64" or "unable to parse" finding; it now emits only `403` or `404`.
* The per-tag SUCCESS checks ("valid key type", "v=DKIM1 first", "RSA key bigger than 1024 bits") are collapsed into one `200` per valid selector.
* URL problems were `FAIL`; they are now `ERROR` (`501`), and a resolver failure on every selector is `ERROR` (`500`) instead of "no keys found".

### What this auditor does not check

* It does not verify a signed message, so a valid key with a broken signer still passes.
* It does not read the optional `t`, `h`, `s` and `n` tags.
* SPF and DMARC are covered by their own auditors; DKIM alone does not tell receivers what to do with unsigned mail.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.