Security & Privacy

SPF, DKIM and DMARC Builder: Why Your Contact Form Mail Vanishes

Three DNS records decide whether your contact form mail arrives: SPF lists the servers, DKIM signs the message, DMARC ties both to the From address a reader sees. What each one checks, why a passing SPF record can still fail DMARC, and where WordPress mail goes wrong.

SPF, DKIM and DMARC Builder: Why Your Contact Form Mail Vanishes

A visitor fills in your contact form. The page says thank you. Nothing arrives. You send a test to your own Gmail address and it lands in spam, or it lands nowhere at all, with no bounce, no error, and nothing useful in any log on the server.

Three DNS records decide that, and they are still evaluated in the order they were invented. SPF (2006, now RFC 7208) lists the servers allowed to send for your domain. DKIM (2007, now RFC 6376) signs the message so a receiver can prove it was not altered on the way. DMARC (RFC 7489, 2015) ties both results to the address a human reads, and says what to do when they disagree.

All three can be published, all three can look correct when you query them, and delivery can still fail. Usually because of alignment: a passing SPF check only counts if the domain SPF checked is related to the domain in the From header your reader sees. Those are two different addresses, and nothing in a default WordPress install makes them the same.

Three records, three different things being checked

Be precise about this early and it saves hours later, because all three are plain TXT records that check nothing alike. SPF is evaluated against the connecting IP and the domain in the SMTP MAIL FROM command, an address that lands in Return-Path and that no mail client shows. DKIM is evaluated against a signature header, using a public key at a selector you choose. DMARC is evaluated last, against the other two results, and it is the only one that reads the visible From.

The builder below runs all three in both directions at once. Fill in the form and it writes the record; paste a record from a DNS answer, quotes and all, and it parses it token by token, explains every term in a sentence and grades what it found.

SPF, DKIM and DMARC builder

Three DNS records decide whether your mail arrives: SPF names the machines allowed to send, DKIM publishes the key that verifies the signature, DMARC says what a receiver should do when neither lines up. Fill a form and the record is written below it, or paste a record you already have and it is read back token by token and graded. Everything happens in this browser tab. The tool never queries DNS, so it cannot follow an include or fetch a key, and it says where that limit bites instead of guessing.

One TXT record at the domain itself. A receiver reads the mechanisms left to right and stops at the first one that matches the connecting address. Everything in it costs DNS lookups except ip4 and ip6, and ten is the hard ceiling.

The domain that sends
Mechanisms, in the order they are read
Put the cheap ones first: a match on an ip4 range ends the check before any lookup happens.
And everyone else

Type over it or paste one from a DNS answer, quotes and all, and the reading below follows what is in the box. Publish exactly one SPF record per domain: two of them is a permerror, and a permerror counts as no SPF at all.

Solid
DNS lookups this record costs0 of 10

TermWhat a receiver does with itLookups

    What this tool cannot do. It never asks a DNS server anything, so an include is counted as one lookup and nothing more, a key is never fetched from the selector you name, and a domain that does not exist looks exactly like one that does. Everything here is the record read as text, which is also how a receiver reads it once it has the answer in hand.

    WunderPaint
    The Dynamic Design and Automation Studio
    WunderPaint is a layered image editor for your WordPress media library. WunderPaint Studio is the same thing in any browser, free and without an account.

    Everything happens in your browser. Nothing you type is uploaded and there is no network request of any kind, which is also the honest limitation: it grades the text of a record, not the reality behind it. It cannot query DNS, so it cannot follow an include or fetch the key at the selector you name.

    SPF, and the address nobody reads

    An SPF record is one TXT record at the domain itself. It starts with v=spf1 and lists terms read left to right until one matches the connecting IP. Each term has a mechanism (a, mx, ip4, ip6, include, exists, ptr) and an optional qualifier deciding the verdict when it matches: + pass, - fail, ~ softfail, ? neutral. Leave the qualifier off and it is a plus.

    v=spf1 ip4:198.51.100.14 include:_spf.google.com include:servers.mcsv.net ~all

    Mail from 198.51.100.14 passes, anything Google’s or Mailchimp’s record vouches for passes, everything else is a softfail. That last term, all, decides every sender you did not list. -all says the list is complete and anything else is forged. ~all says probably not us, accept but note it. ?all says nothing at all, and +all authorises the entire internet to send as you, which is worse than publishing no record.

    One detail matters more than the endless -all versus ~all argument: DMARC only cares whether SPF returned pass. Softfail and fail are both not-a-pass, so they behave identically for alignment. That is why moving from ~all to -all so often changes nothing you can see.

    The remaining faults are structural. Two SPF records on one domain is a permanent error, not a merge. A second all is dead code, and any term after all is never reached. A redirect= modifier is ignored entirely when an all is present, so the two together are almost always a mistake. A missing v=spf1 means the record is not an SPF record at all.

    DKIM, the signature that travels with the message

    DKIM adds a header to every outgoing message: the signing domain in d=, the selector in s=, the headers covered in h=, the signature in b=. A receiver queries selector._domainkey.yourdomain.com, pulls the public key out of the p= tag and verifies. Change the body or a covered header in transit and the signature breaks.

    selector1._domainkey.example.com. IN TXT (
      "v=DKIM1; k=rsa; h=sha256; "
      "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." )

    The p= value is not raw key material. It is base64 of a DER SubjectPublicKeyInfo: a wrapper naming the algorithm, then a BIT STRING holding a SEQUENCE of two INTEGERs, the modulus and the exponent. That is why the builder walks the DER down to the modulus instead of measuring the base64, which overshoots by the size of the wrapper, roughly thirty bytes. A 2048 bit RSA key serialises to 294 bytes of DER and exactly 392 characters of base64; a 1024 bit key to 162 bytes and 216 characters. An Ed25519 key under RFC 8463 is different again: 32 raw bytes, 44 base64 characters and k=ed25519. To see those bytes yourself, the base64 decoder in the hashes and encoding tool takes a p= value straight out of a record.

    RFC 8301 set the floor at 1024 bits, the target at 2048, and forbade rsa-sha1 outright, so h=sha1 is a finding worth acting on. Two flags are easy to leave on by accident. t=y is test mode: receivers treat the signature as if DKIM were not deployed, so a broken signature costs nothing and so does a working one. t=s means d= must match exactly, no subdomains. And an empty p= is not a typo, it is a revocation.

    One quirk trips people up in DNS panels. A single character-string holds at most 255 octets and a 2048 bit key cannot fit, so the record is published as several quoted strings inside one TXT record, joined end to end by the resolver. Most panels split it for you, a few reject the value. The builder writes the split out and treats it as information rather than a fault.

    DMARC, the record that reads the From header

    DMARC lives at _dmarc.yourdomain.com, must start with v=DMARC1 and must carry a p= policy. Everything else has a default, and the defaults are in force whether or not you write them:

    • sp inherits p, so a subdomain policy exists even when you never wrote one.
    • adkim and aspf default to r, relaxed alignment.
    • pct defaults to 100 and applies only to quarantine and reject.
    • ri defaults to 86400 seconds, one aggregate report per receiver per day.
    • fo defaults to 0 and does nothing without a ruf address to send to.

    rua is the address for aggregate reports: daily XML naming every IP that sent mail claiming to be you, and whether each passed SPF, passed DKIM and aligned. It is the most valuable tag in the record. ruf asks for per message failure reports, which most large receivers stopped sending years ago on privacy grounds. Both need the mailto: scheme spelled out. And the record that does nothing and tells nobody is v=DMARC1; p=none with no rua at all. It is valid, it satisfies a checkbox on a security questionnaire, and it produces neither protection nor information.

    One reporting rule surprises everybody. If rua points at a mailbox on a different domain, that domain must authorise it by publishing yourdomain.com._report._dmarc.reportingdomain.com containing v=DMARC1. Without it a conforming receiver will not send, and you wait for data that was never coming. The builder flags any off-domain reporting address, which is exactly the case where a hosted DMARC service is involved.

    Alignment, the part everybody misses

    DMARC passes if at least one of SPF and DKIM passes and is aligned with the From header. Not both. One is enough, which is the mercy in the design, but the alignment half is not optional.

    Table of five sending scenarios showing envelope sender, header From, SPF result, DKIM signing domain and the resulting DMARC verdict, above a five step flow of SPF check, SPF alignment, DKIM check, DKIM alignment and policy applied

    SPF alignment compares the envelope sender domain with the From domain. DKIM alignment compares d= with the From domain. Relaxed alignment, the default, accepts a match at the organisational domain, so mail.example.com aligns with example.com. Strict alignment demands identical labels.

    Now the WordPress case. When PHP hands a message to the local sendmail binary, the envelope sender becomes whatever the local mail transport decides, typically the web server user at the server’s own hostname: www-data@srv12.hostingcompany.net. SPF for that hostname may well pass, because the server really is allowed to send its own mail. But the From header says wordpress@yourdomain.com. Different organisational domain, no alignment, no DKIM signature to fall back on. SPF passed, DMARC failed, and under p=reject your own contact form mail is unroutable while every SPF checker you try reports green.

    The second classic failure is a contact form that sends as the visitor so that hitting reply works. The message claims a From of someone@yahoo.com, sent from your web server, which is not in Yahoo’s SPF record and cannot produce Yahoo’s DKIM signature, against a domain publishing p=reject. That mail is refused, not filtered. Put your own address in From and the visitor in Reply-To.

    The ten lookup limit

    RFC 7208 section 4.6.4 allows at most ten DNS-querying terms while evaluating a single SPF record. Exceed it and the check must return permerror, which is not a pass, which under DMARC means SPF contributed nothing at all.

    Table of SPF terms with the DNS lookup each one costs, from zero for ip4, ip6, all and exp to one for a, mx, exists and ptr and one plus everything inside for include and redirect, with the ten lookup budget, two void lookups and 255 octet character-string limit shown as figures

    The trap is that the budget is spent recursively. An include costs one for itself plus everything inside the record it points at. include:_spf.google.com has long expanded to three further _netblocks includes, so it takes four of your ten, not one. Add a newsletter platform two deep, then a helpdesk, then an invoicing tool, and you reach the ceiling with no warning. The record that worked for six years breaks the day somebody appends one more include, and every sender in it stops counting at once.

    Two smaller caps sit alongside. An mx mechanism may not trigger more than ten address lookups of its own, and the evaluation is allowed at most two void lookups, queries returning no answer, which is how a record rots as old providers get decommissioned. ptr is deprecated outright and should not appear in a record written today.

    Because the builder never touches the network, it counts each include, exists and redirect as exactly one and names every one it could not follow. The finding says so plainly: one is the floor, not the price. Fetch that record, paste it into the box, count it too. Flattening includes into a list of ip4 ranges does get you under ten, and it breaks silently the next time a provider changes addresses.

    WordPress mail, and why an SMTP plugin is the fix

    Look at what core actually does. wp_mail() builds a PHPMailer object and, unless a plugin intervenes on phpmailer_init, calls $phpmailer->isMail(). That is PHP’s mail() function, which hands the message to a sendmail binary on the box. Nothing in that path signs anything, nothing sets a sensible envelope sender, and no mail service is involved that could vouch for you.

    The default sender is built in wp-includes/pluggable.php: the name is WordPress and the address is wordpress@ plus the host of network_home_url() with a leading www. stripped. Core’s own comment admits some hosts block outgoing mail from that address because the mailbox does not exist, and bounces to a mailbox that is not there go nowhere.

    The fix for almost every deliverability problem on a WordPress site is to stop sending from the web server. An SMTP plugin makes wp_mail() authenticate to a real mail service instead. That service sends from IPs already in its own SPF record, signs with a DKIM key for your domain once you publish the selector it gives you, and lets you set a From address that is a real mailbox. Envelope and header then share a domain, and alignment stops being something you think about.

    Two practical notes. An SMTP password stored in plugin options lives in the database, and in every backup; most SMTP plugins accept PHP constants instead, and wp-config.php is the right place for credentials like that. And wp_mail() is synchronous, so the handshake happens inside the request that triggered it: a form submit can wait a second or more on a remote mail server, worth remembering when you are working out why a site feels slow. Plugins that queue mail to avoid that lean on WP-Cron, which only fires when somebody visits.

    A rollout that does not lose mail

    Publish p=none with a rua address first and change nothing else. None means no receiver alters its handling, so nothing can break, while every participating receiver starts sending daily XML naming the IPs that send as your domain. That list is always longer than expected: the CRM, the invoicing tool, an old plugin on a staging site, a form on a subdomain you forgot existed.

    _dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; sp=none; adkim=r; aspf=r"

    Stay there until you can name every source in the reports and every legitimate one passes aligned. Then move to p=quarantine, optionally with pct below 100 to sample, though treat pct as a hint: receivers apply it inconsistently, it affects only quarantine and reject, and the current DMARC revision work drops it. Then reject. Set sp deliberately at each step, because the subdomains you never send from are precisely what gets spoofed.

    The shape should feel familiar if you have ever switched on a strict browser policy: publish in monitoring mode, read what breaks, then enforce. It is what Content Security Policy report-only mode exists for, and it works for the same reason, because the reports name the legitimate traffic you forgot about before you start blocking it.

    What the builder deliberately will not tell you

    No network means no DNS, and that shapes the honest edges. It cannot see a second SPF record on the same domain, an instant permanent error however well each is written. It cannot fetch the key at the selector you name, so it grades the key you paste, not the key that is published. It cannot count void lookups, and the domain fields serve the label and the external reporting check, nothing more.

    Other omissions are on purpose. There is no key generation, because the private half of a DKIM keypair has no business in a browser tab. There is no BIMI, no MTA-STS and no ARC. The “Read it into the form” direction drops terms the form cannot represent, such as redirect=, exp= and unknown tags, naming each one in the status line rather than handing back a record that quietly lost something.

    Where this leaves you

    Mail authentication feels arbitrary because it looks like three independent checkboxes and behaves like one system with a hidden joining condition. SPF and DKIM are evidence. DMARC is the only part that asks the question anybody cares about: whether the name on the From line has anything to do with whoever sent the message. Publish SPF and DKIM without DMARC and you have collected evidence nobody will weigh. Publish DMARC without understanding alignment and you will enforce a policy against your own contact form.

    For a WordPress site the order is short. Move mail off the web server onto a service that will sign for your domain. Set From to a mailbox that exists and keep the visitor in Reply-To. Publish SPF with a single deliberate all and count the lookups before adding an eleventh source. Publish DKIM with a 2048 bit key and no lingering t=y. Then publish p=none with a reporting address and read what comes back for a month.

    The reports are the part that keeps paying off, because a domain’s sending profile drifts. A tool gets added, another gets cancelled, a plugin starts sending from somewhere unexpected. A record graded green today and never looked at again is how a domain ends up at eleven lookups, one revoked selector and a policy that rejects the only mail you wanted.

    SPF, DKIM and DMARC Builder: Why Your Contact Form Mail Vanishes

    Table of Contents

    Learn it by building something

    Every week one thing you can make the same afternoon, from dynamic templates to 3D type. Written down step by step.

    One mail a week, and then it ends.
    Unsubscribe in one click.

    Troubleshooting

    Why Your WordPress Images Look Blurry, and Which Cause It Actually Is

    Blurry WordPress images are not one problem but six, and each leaves a different fingerprint. Work out which symptom you have before you change anything, because most of the fixes do nothing for most of the causes.

    SEO & Structured Data

    Redirect Rule Generator: The Rules That Never Fire

    A redirect list rarely breaks. It accumulates: a broad prefix rule that kills every rule below it, a target missing a trailing slash that doubles your hop count, a 301 where a 308 was needed. Generate the Apache, nginx and CSV versions of your rules, then walk a URL through them and watch where it actually goes.

    Photo Editing

    Spend your JPEG bits where the eye looks, not evenly across the frame

    An encoder gives the hedge behind your subject as many bits as the subject. A saliency model knows better, but a browser will not let you set quality per region, so the encoder is not steered: its input is prepared instead. On the sample that was 41 per cent off the file at an unchanged quality setting.

    Troubleshooting

    HTTP Error When Uploading Images to WordPress: The Real Causes

    The red bar reading HTTP error is not a verdict on your image. It is the uploader reporting that the POST to async-upload.php came back unusable, and six unrelated failures produce exactly that. Here is how to tell them apart in about ten seconds.

    Print & Craft

    From Screen to Paper: What Actually Changes When a Design Gets Printed

    Resolution that is not a property of the file, colors that cannot be mixed in ink, and a blade that never lands exactly on the line you drew.

    WordPress Development

    Dynamic Templates: Build the Design Once, Let Your Posts Fill It In

    You build the layout once, tell a few layers where to get their content, and from then on every post, product or page renders its own version.

    Download the free WunderPaint Plugin for WordPress

    The WunderPaint workspace with the layers panel, adjustment sliders, text style presets and the asset library along the bottom

    The Image Editor & Design Studio

    Everything described here can be done in the browser, on your own site. The live demo runs the full editor with nothing to install.

    Free

    Chaos Art

    Autonomous painters make one-of-a-kind abstract art in 3D space - gestures, art movements, painterly media, and embeds that paint a new original for every visitor.

    Pro

    Particle Strokes

    Paint with swarms of light: twenty-two movements, a stamp you draw yourself, and curves that give a stroke a shape - the swarm keeps painting for a few seconds after you let go.

    Pro

    City Diorama

    Any place on earth as a miniature you could hold: real streets, water and building footprints raised into a 3D diorama - or wrapped around a sphere as your own tiny planet.

    Free

    Papercut Art

    Layered paper pictures with real depth - a photo sliced along its actual depth into up to twenty layers, parametric landscapes, animals and clouds you can shape, letters with real counters, and a look that runs on three dials.

    Pro

    3D Earth Studio

    A hyperrealistic globe - day and night with city lights, live clouds, atmosphere halo, country borders and highlights, click-to-place markers with flight-route arcs, satellite orbits, seamless rotation video and a live website embed.

    Free

    Mystic Studio

    Turn a birth date into wall art - a real natal chart with houses and aspects, the moon of that night, zodiac and Chinese zodiac posters, numerology cards and a synastry wheel for two, in eight artful themes.

    Free

    Marble Bath

    Marble paper on a virtual water bath - drop, rake and comb real Ebru patterns with gestures, flowers and classic recipes, razor-sharp at any size and re-editable as a layer.

    Free

    Day Ring

    Turn a day into a beautiful circular schedule - colour-coded time blocks as arcs around a 24-hour clock, with concentric rings for overlaps, emoji, templates and a legend.

    Free

    Code Shot

    Turn code into a gorgeous, share-ready image - syntax highlighting, editor themes, window frames and diff highlighting - then drop it into your design as a re-editable layer.

    Pro

    3D Solar System Studio

    Build a date-accurate 3D solar system - real planet positions for any date, photoreal textures and one slider from artistic to true scale - then drop it into your design as an editable layer.

    Pro

    3D Molecule Studio

    Build a real 3D molecule - from a curated library, the periodic table or a SMILES string - then style it, measure it and drop it into your design as an editable layer.

    Pro

    3D Textile Studio

    Drop your design onto cloth that behaves like the material you pick: silk falls soft, felt holds its shape, flag fabric snaps in the wind. Hang it, blow it and drape it, then lay the finished drape back into your document as a picture.

    Pro

    3D Particle Studio

    Point the engine at any layer and it becomes a cloud of particles that keeps its colours, flowing through a sphere, a galaxy or your own outline. Keep the frame you like as a still, or embed the running engine so it keeps moving on your page.

    Free

    Origami

    Put your own picture on the paper and watch that very sheet fold itself into a crane or a box. Every step is a station you can stop at and turn around in 3D, which is exactly where printed diagrams leave you alone.

    Pro

    3D Flip Studio

    A hardcover you can leaf through, a limp magazine, a strewn pile of sheets, a sticker peeling off its backing. The curl is real geometry, so the print never slides across the paper.

    Free

    Handwriting Fonts

    Draw the alphabet here or fill in a printed sheet and photograph it. What comes out is a genuine font family, installed into your site and available in every picker.

    Pro

    Step Guides

    Turn any picture into an instruction. Every mark is pinned to a place in the image, so arrows still point at the right thing after the callout has been dragged somewhere else.

    Free

    Text Art

    One studio, sixteen art types: ASCII and emoji art, brick, dice, cube, sticky-note, LED, ceramic and keycap mosaics, word portraits, text flows, silhouettes, element tiles and more.

    Free

    Photo Mosaic

    The classic photomosaic, computed in your browser: your main image emerges from many media-library photos via true structure matching - never a cheap overlay.

    Free

    Puzzle Sheets

    Generate printable puzzle sheets: word search, mazes in eight shapes, sudoku with unique solutions, criss-cross, cryptograms and number pyramids.