DKIM Record Checker

Fetch the DKIM key a selector publishes and read it tag by tag, with the key size measured rather than guessed.

Secure server processingEmailWorkingNo account needed

What this tool does

A DKIM key is not published at your domain. It sits at selector._domainkey.domain, where the selector is the short label your signing service picks, and a domain can carry as many keys as it has selectors. Give this page a domain and a DKIM selector and the OmniKitly service assembles that name, resolves the TXT record at it with its own resolver, and the record comes back parsed: a table naming every tag, its meaning and its value, with a one-line summary beneath it giving the key type, the size in bits and the number of faults counted.

Parsing starts by putting the record back together. Surrounding quotes are stripped and adjacent quoted strings are joined before the tags are split on semicolons, so a key published in pieces reads the same as one written on a single line. Only single-letter tags are read, so anything longer is quietly skipped; the ones that are known — v, k, p, t, h, s, n and the obsolete g — are listed with their meaning in the order they appear, and p= is shortened to its first 40 characters with its full length after it. That p= value is base64-decoded and walked as DER, so the size is measured: an RSA modulus counted to the bit, or a 32-byte Ed25519 key reported as 256 bits. An empty p= is read as a revoked key rather than an error, while a p= that is not a DER-encoded key defeats the parser altogether, and the pane then falls back to the service's raw JSON.

The checks behind that summary are where the tool is currently weakest. It counts a fault when v= is not the first tag, when the version is not DKIM1, when k= names a key type it does not know, when an RSA key falls under the 1024 bits RFC 8301 requires, when h= permits only SHA-1, when s= lists service types without email, and when a tag appears twice; it counts a note for t=y testing mode, t=s strict alignment, a 1024-bit key that still verifies while 2048 is the current recommendation, and a record over 255 characters. Every one of those sentences is written and then thrown away by the result pane, which shows the tag table and drops the report that belongs beside it. What reaches the screen is the tally — "1 issue" — so treat it as a pointer back to the list above and read the t=, h= and s= rows of the table yourself.

This is a server tool with no file in it. The two strings it needs, the domain and the selector, are sent to the OmniKitly service as job settings over an encrypted connection, the query is issued by that machine's resolver, and the job row and its JSON answer are swept together when the job expires — six hours later on the setting this build ships with. One consequence of the query happening there: the input box invites a record as well as a domain — the label, the placeholder and Load sample all offer one — but every run is submitted to the resolver as a host name, so a pasted record is refused with "That is not a valid hostname." and the sample cannot be run at all. The parser those controls point at is real; it is what reads the record the lookup fetched. It just has no route from the box.

Two things no version of this can settle. Nothing here resolves what a selector should be: DNS offers no way to list the names under _domainkey, so a domain's selectors cannot be enumerated by anything. And this reads a published key, never a signature — it does not hold your private key, so it cannot confirm the two are a pair, and it cannot tell you whether any particular message verified. That verdict is recorded by the receiving server, and Email Header Analyzer is where you read it.

How to use it

  1. Enter the domain that publishes the key. The domain on its own, not the record and not the _domainkey name. A URL is cut back to its host first, so https://example.com/pricing and a trailing-dot example.com. both arrive as example.com.
  2. Set the selector. The s= value from a DKIM-Signature header — selector1, google, k1. Characters outside letters, digits, dots, hyphens and underscores are dropped from it, and it defaults to "default", which plenty of providers never use, so take it from a real message rather than guessing.
  3. Read the summary line under the output. It gives the key type and the size in bits, or opens with "Revoked key" when p= is empty, and ends with either a fault count or "valid record". The tag table above it carries the values those checks were run against.
  4. Open the JSON to see the name that was queried. The button beside the table downloads the service's own report — the host, the exact selector._domainkey name it resolved, and every TXT string found there. The parsed view does not repeat that name.

Processing & privacy

Secure server processing. This operation requires temporary server processing.

Common use cases

Turning on a new sending service

Providers hand you a selector and a key to publish. Querying it confirms DNS has propagated before you move live traffic and start relying on the email signing.

Chasing a dkim=fail you were sent

Take the s= and d= values straight from the DKIM-Signature line and query that exact pair. An empty answer usually is the explanation.

Rotating a key

Confirm the incoming selector publishes a 2048-bit key before you point the signer at it, then check that the retired selector now summarises as a revoked key rather than a live one nobody is watching.

Auditing a domain you inherited

Selectors accumulate over years of marketing tools. The bit count in the summary and the t= and h= rows in the table show which are still 1024-bit, sitting in testing mode, or offering SHA-1 alone.

Questions

Do I have to know the selector?
Yes, and there is no way round it. DNS offers no listing of what exists under _domainkey, so nothing can discover a domain's selectors for you. Read it from the s= tag of a DKIM-Signature header in a message the domain sent, or from your provider's setup page. Left alone the field submits "default", which is rarely where a real key lives.
Can I paste a record instead of a domain?
Not today, although the input label, the placeholder and the Load sample button all offer it. Every run is handed to the DNS resolver as a host name, and a record — with its spaces, semicolons and equals signs — fails that check and comes back "That is not a valid hostname." Give it the domain plus a selector instead, and the record is fetched and then parsed for you.
The lookup came back with nothing. Is the record missing?
Not necessarily, and the tool cannot tell the difference. A wrong selector, a key published under a different domain, a resolver failure and a genuine absence all end at the same empty answer. What you get back is the raw JSON, which names the exact host it queried, so check that string against the s= and d= values from a signed message before concluding the key is gone.
Why do I see a count of issues but not the issues?
Because the result pane renders the tag table and discards the written report that goes with it, and Copy output gives you the table as CSV. The checks themselves run — the tally in the summary is real — but their wording never reaches the page. Until that is fixed, the list in the third paragraph above is what the count is referring to.
Does this prove a message's signature is valid?
No. It reads the public key a selector publishes. Verifying a signature needs the message's headers and body as they were signed, which this never sees. The receiving server does that work and records the result, which Email Header Analyzer will show you.
What does an empty p= mean?
The key is revoked. Any signature made with that selector will fail verification, which is the intended outcome when a selector is retired — so it is a correct answer when you are checking that an old key really is out of service, and a problem when you expected a live one.
Is a 1024-bit RSA key still acceptable?
It still verifies, and it is counted as a note rather than a fault, so the summary can read "RSA 1024-bit key · valid record" and leave the judgement to you, with 2048 bits the current recommendation. Below 1024 it becomes an issue, because RFC 8301 has verifiers reject it. Ed25519 keys are 32 bytes, reported as 256 bits, and are measured against neither threshold.
Is my domain sent anywhere?
Yes. The domain and the selector go to the OmniKitly processing service over an encrypted connection, because a browser cannot make a DNS TXT query itself. Both are stored with the job record, and that record and its JSON report are removed together when the job expires, which the shipped setting puts six hours out. No document is involved, so none is uploaded.
Why is my record split across two quoted strings?
A single DNS character-string cannot exceed 255 bytes, and a 2048-bit RSA key comfortably passes that, so the record is published in pieces. Adjacent quoted strings are joined before the tags are read, and the resolver returns the pieces already concatenated, so the split makes no difference to what you see.

Related tools