Operations and security
Why the message from your contact form never arrives
The form says “Thank you, your message has been sent.” There is nothing in the inbox.
9 min read
By Timo Wessels Published
The form says “Thank you, your message has been sent.” There is nothing in the inbox.
Both are true at the same time, and that is the whole problem. The website handed the message over. What happened after that, it never found out.
If you are reading this article because a customer told you they got in touch and never received an answer: that is no small thing. Every enquiry that disappears is a job someone else got.
What “sent” actually means
A website does not send emails. It hands a message over to something else that is supposed to send it.
The success message in the form only says: the handover worked. It says nothing about whether the message left the building, whether it was accepted along the way, or whether at the destination it landed in a folder nobody looks at.
That is not carelessness in the form. It is because at this point the website genuinely learns nothing more — the answer comes seconds to hours later, somewhere else, and nobody is listening any more by then.
The chain has four stations
First, the form hands over to the website. Second, the website hands over to a sending route. Third, the recipient's server decides whether to accept the message. Fourth, the mailbox decides which folder it goes into.
Any station can fail. None of them reports it.
Station two is the most common cause
The built-in sending function that many systems use by default is the weakest point in the chain.
It assumes that a mail service is running on the server itself. On many hosting packages that is the case, on some it is not. If there is none, nothing is sent — and the website does not notice.
And even if there is one, it sends under the identity of the server, not that of your domain. To the recipient that looks like mail from a sender who claims to speak for you.
That is why this function is generally considered unsuitable. It is awkward to configure, it requires manual work in places a proper sending library handles by itself, and it is the cause of a large share of all form messages that never arrive.
The mistake almost every self-built form makes
This is where it gets concrete, and this point alone explains many cases.
A form asks for the visitor's email address. The obvious thing is to enter this address as the sender of the notification — then you can click “Reply” directly in the mailbox.
That is convenient and technically wrong.
Because your server then sends a message that claims to come from a foreign domain. The receiving server checks whether your server is allowed to do that. It is not — it cannot be, because the foreign domain knows nothing about it.
And even if your own domain is set up cleanly, that does not help here: the check requires the visible sender domain to match the domain that authorised the sending. If a foreign address stands in the sender field, nothing matches. The check fails even though your records are fine.
The right way: your own address is the sender. The visitor's address goes in the reply-to field. The reply button works just the same, and the check passes.
That is a setting, not a rebuild. And it is set wrong on a frightening number of forms.
Station three decides on the basis of your domain
What happens next no longer depends on your website but on records at your domain. Whether the sending server may speak for you, whether the message stayed unchanged on the way, and what should happen to it if one of these checks does not work out.
That is a topic of its own and has an article of its own. For here one sentence is enough: A form can be technically flawless and still deliver nothing if these records are missing or do not match the sending route.
Station four: your own mailbox
The least spectacular case, and it happens often enough to check it first.
Messages that always look the same, always come from the same sender and are never answered move into the spam folder after a while. Or a filter rule someone set up years ago sorts them into a subfolder nobody looks at any more.
Before you change anything on the website: look in the spam folder and check your filter rules.
The two proper sending routes
There are two routes that actually work, and they differ in what they give back to you.
The first route: the website logs in to a real mail server and hands the message over there — with address, credentials and an encrypted connection, just as a mail program does. The message then goes out under your domain's identity, because it really does come from your mailbox.
That is the obvious route. It is set up in a few minutes and solves most cases.
The second route: a sending service that specialises in nothing else. The website hands over through an interface. That is faster than the first route, because no login to a mail server takes place, and it scales better.
The price: for such a service to be allowed to send under your domain, records have to be set at the domain. That is the more laborious part of the setup, and it is the reason many stay with the first route.
What the second route gives back
And here lies the point that makes the matter interesting: a log.
A mail server on its own does not tell you what became of a message. It accepts it, and then there is silence. A sending service keeps books: delivered, rejected, undeliverable. With a rejection, the reason is given.
One category of these is especially valuable: the message to an address that does not exist. The receiving server rejects it hard, and the sending service writes that down.
That closes a gap that cannot be closed in the form itself. When filling in a form, no method can establish whether a typed-in address exists. A typo in your own address looks like a valid entry. Only the attempt to reply there brings it out — and without a log you never find out about that either.
Such reports can be received automatically instead of searching for them in an interface. Then you are told when a reply could not be delivered, instead of taking it for a customer who is not getting back in touch.
How to narrow it down yourself in twenty minutes
In order, and the order is not arbitrary — it goes from the cheapest to the most laborious.
First: spam folder and filter rules. Costs two minutes and explains a share of all cases.
Second: fill in the form from outside. Not logged in, not with your own address as the sender, but the way a stranger would. Does anything arrive?
Third: look at the headers of the message that arrived. Most mailboxes have a view for this that shows the original. It says which server sent it and whether the checks passed. If it says “pass” three times, it is not the sending.
Fourth: look in the form at what is entered as the sender. If it is the visitor's address, you have found the cause.
Fifth: check which sending route the website uses. If nothing is set there, it uses the built-in function — and that is the most likely suspect.
What to do
Sender to your own address, visitor's address into the reply-to field. Costs nothing and fixes a whole class of failure.
Switch from built-in sending to a real mail server. The standard route, done in a few minutes, and it solves most of the remaining cases.
Check whether the records at the domain match the chosen sending route. That is the point where a cleanly set-up domain can still fail when a new sending route was not entered.
If enquiries are your business: take a sending service with a log. Not for the speed, but because then you notice when something does not arrive — instead of not noticing.
And then a reminder in the calendar. A form that worked for a year can quietly stop working after a server move, a change of host or a change to the domain records. Filling it in from outside once a quarter takes a minute.
The core
A contact form is not a finished thing you build in once. It is the start of a chain whose remaining links lie outside the website.
And it is the only function of your website where a failure is completely silent. A broken page you can see. A broken form says “Thank you”.
Sources
- Atlas,
php/udemy-sending-email-with-php-from-basic-to-advanced/02— that the built-in sending function requires a local mail service that is not present on every system; that configuring it requires access to server files that one does not always have; that it expects line breaks and headers by hand, makes attachments awkward and easily creates security holes; that using it directly is advised against and common systems therefore use sending libraries; and the components of a login to a mail server — address, port, credentials and encrypted connection — and the option to switch on detailed log output when there are connection problems. - Atlas,
php/udemy-sending-email-with-php-from-basic-to-advanced/07— that a sending service offers an interface besides the login to a mail server, which is faster and scales better; that sending under your own domain this way requires setting records at the domain and that this part is laborious; that a mail server on its own provides no tracking, whereas a sending service logs delivery, rejection and undeliverability; that a hard rejection means the address does not exist; and that such events can be reported automatically to your own address instead of searching for them in an interface. - Atlas,
services/email-deliverability/dmarc.md— that the check only passes when one of the two authorisations passes and the authorising domain matches the visible sender domain; that a passed authorisation for the technical sender address still leads to a failure if it does not match the visible sender address; and that exactly this mismatch arises when a sender sends under its own technical address while another domain stands in the visible sender field. - Reference instead of repetition — which three records at a domain authorise sending and how they work together is in the article on mail that lands in spam. Why a form can never establish while it is being filled in whether an address exists is in the article on how forms are built.
- Own audit practice — the order of narrowing down from the cheapest to the most laborious, the advice to look in the spam folder and the filter rules first, the check from outside with a foreign address, reading the headers of the message that arrived, and the quarterly repetition, because a form can quietly stop working after a move or a change to the domain records.