Why Your Website Emails Land in Spam, and How to Fix It

Your contact form works, the site says message sent, and the email quietly lands in junk. The cause is almost never the form. It is how your server authenticates mail, and it is fixable in an afternoon.
Here is how most people find out. A customer calls, a bit annoyed, and asks why nobody replied to the quote request they sent two weeks ago. You check the form. It works. You send yourself a test and the site says message sent. Nothing arrives. You find it later, sitting in junk, next to an offer for discount printer ink.
The part that stings is not the one lost enquiry. It is that you have no idea how long this has been going on, or how many people gave up quietly before that one customer bothered to pick up the phone.
The form is almost never the problem. Your server is sending mail in a way that inbox providers no longer trust, and that is fixable in an afternoon.
What is actually going wrong
When your website sends an email, the receiving server asks one question before it asks anything else. Is this machine allowed to send mail claiming to be from this domain. If the answer is no, or just unclear, the message gets filed as suspicious. Usually not rejected, because a rejection would at least tell you something. Just quietly buried.
On shared hosting there are three common reasons the answer comes back unclear.
- Your site calls PHP mail(), which hands the message to the server's local mail program with no authentication attached. It is the equivalent of dropping an unsigned letter in a postbox and hoping.
- Your host signs outgoing mail with one shared DKIM key covering every customer on that server. Your sending reputation is now the average of a few hundred strangers, and some of them are sending things you would rather not be averaged with.
- Your From address says yourbusiness.com but the machine sending it is mail47.somehost.net. To a receiving server that mismatch looks exactly like spoofing, because that is what spoofing looks like.
None of this shows up anywhere in your admin panel. The form logs a success, because handing the message off was a success. What happened after that was somebody else's decision.
Diagnose it in about ten minutes
Before you change anything, find out what the receiving side actually sees. Guessing here costs days.
- Open mail-tester.com, copy the address it gives you, and send a message to it through your own contact form. It scores you out of ten and, more usefully, lists line by line what passed and what failed.
- Send a test to a Gmail address you own. Open it, click the three dots, choose Show original. You will see three lines, SPF, DKIM and DMARC, each saying pass or fail. That is your whole diagnosis on one screen.
- Run your domain through MXToolbox to see whether an SPF record exists at all, and whether your sending IP has landed on a public blocklist.
Most of the time at least one of those three is failing. Often all three are missing entirely.
The three DNS records that decide this
These are TXT records you add wherever your domain is managed. They are not code and they do not touch your website. They are public statements about who is allowed to send mail as you.
SPF, who is allowed to send
SPF lists the servers permitted to send using your domain. One record, at the root of the domain, and only one. A second SPF record does not add to the first, it breaks both. I find that mistake constantly.
v=spf1 include:_spf.google.com include:sendgrid.net ~allEvery service that sends on your behalf needs an include entry, your mail host, your marketing platform, your website's relay. Miss one and that service starts failing. The ~all at the end means treat anything else as suspicious rather than rejecting it outright, which is the sensible setting while you are still confirming things work.
DKIM, proof the message is really yours
DKIM attaches a signature to every message you send, and the receiving server checks it against a public key published in your DNS. It proves the message came from you and was not altered on the way. Your email provider generates the key and hands you the record to paste in. What matters is that it is your key on your domain, not the shared host key that three hundred other sites are also using.
DMARC, what happens when something fails
DMARC ties the other two together and tells receiving servers what to do with a failure. It also sends you reports, which is how you find problems before your customers do.
v=DMARC1; p=none; rua=mailto:[email protected]Start at p=none. That means watch and report, change nothing. Sit there for a few weeks, read the reports, confirm every legitimate sender is passing, then tighten to p=quarantine and eventually p=reject. Going straight to p=reject before you know all your senders is how people end up blocking their own invoices.
Alignment is the part people miss
You can have all three records in place and still fail. DMARC does not only ask whether SPF and DKIM passed. It asks whether they passed for the domain in your From address. That is alignment.
If your form sends from [email protected] but SPF passes for somehost.net, that is a technical pass and an alignment failure, and DMARC counts it as a failure. This is the single most common reason someone tells me they already set all this up and mail still lands in junk. Whatever address you send from has to belong to the domain you authenticated.
Why this got stricter, and why it is not your fault
If your emails used to arrive fine and started failing somewhere in the last two years, you did not break anything. The rules changed underneath you.
In February 2024, Google and Yahoo began requiring authentication on mail sent to their consumer inboxes, with the strictest tier applying to anyone sending more than 5,000 messages a day. Microsoft followed on 5 May 2025 with equivalent requirements for Outlook.com, Hotmail.com and Live.com, routing mail to Junk when a high volume sender did not comply, and saying outright rejection would come later.
That threshold means a small business is not formally in scope, and it is tempting to stop reading right there. The catch is that the filters were retrained on the assumption that legitimate senders authenticate. Being technically exempt does not help you when the classifier has learned that unauthenticated mail is usually junk. In practice this is everybody's problem now.
Stop using PHP mail and send through a relay
Once the DNS is right, the last piece is sending through a service built for deliverability instead of through your web server. A relay gives you your own DKIM signature, warm sending IPs, bounce handling, and logs that tell you what actually happened to each message.
One correction, because plenty of guides still say otherwise and an earlier version of this article did too. SendGrid no longer has a permanent free plan. Twilio retired it starting 27 May 2025. New accounts get a 60 day trial at 100 emails a day, after which paid plans start around $19.95 a month. If you followed a tutorial promising a free forever tier, that tutorial is out of date.
As of 2026 the options with real permanent free tiers include Brevo at roughly 300 emails a day, Resend at around 3,000 a month, and Amazon SES, which is by far the cheapest at volume but expects you to be comfortable inside AWS. Postmark and Mailgun are both solid paid choices when reliability matters more than price. Check the numbers on the provider's own site before you commit, because this part of the market moves, which is exactly how this article ended up recommending a plan that had stopped existing.
Setting it up on WordPress
Install WP Mail SMTP or Fluent SMTP. Both are free, and both do the same core job. They intercept every email WordPress tries to send and route it through your relay instead of the local mail program.
- Create an account with your provider and verify your sending domain. This is where they hand you the DKIM records for DNS.
- In the plugin, pick your provider and paste in the API key.
- Set the From address to a real mailbox on your own domain. Not noreply@, and not an address on the hosting company's domain.
- Send the plugin's test email, then run one more through mail-tester to confirm all three lines pass.
Setting it up on custom PHP
Do not call mail() directly. Use PHPMailer or Symfony Mailer over authenticated SMTP, which handles encoding, TLS and error reporting properly.
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.yourprovider.com';
$mail->SMTPAuth = true;
$mail->Username = getenv('SMTP_USER');
$mail->Password = getenv('SMTP_PASS');
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->setFrom('[email protected]', 'Your Business');
$mail->addReplyTo($customerEmail, $customerName);
$mail->addAddress('[email protected]');
$mail->Subject = 'New enquiry from the website';
$mail->Body = $message;
$mail->send();Look at the split between setFrom and addReplyTo. The From address stays on your authenticated domain so alignment holds, and Reply-To carries the customer's address so hitting reply still reaches the right person. Putting the customer's address in From is the classic mistake, and it fails DMARC every single time, because you are not authorised to send as their domain.
Keep credentials in environment variables, never in the repository. An SMTP key sitting in a public repo becomes somebody else's spam relay within days.
The mistakes that keep people stuck
- Two SPF records on one domain. They do not combine, they cancel. Merge everything into one.
- Sending from an address that is not a real mailbox. If a reply bounces and nothing is there to receive it, that counts against you.
- Forgetting a sender. Your CRM, your invoicing tool and your booking system all send as your domain, and all of them belong in SPF.
- Jumping to p=reject before reading the reports, then wondering why the accounting software stopped delivering.
- Fixing the DNS but leaving the site on PHP mail(), so nothing gets signed and none of the new records apply.
How you will know it worked
DNS changes land within minutes to a few hours. After that, run a fresh test through mail-tester and look for nine or ten out of ten with all three lines passing. Then open a real message in Gmail, choose Show original, and confirm the same three.
Reputation is slower. If your domain has been sending unauthenticated mail for months, the filters carry that history for a while. Give it two to three weeks of clean sending before you judge the result. Your DMARC reports will show the trend before your inbox does.
If you would rather not spend an afternoon inside DNS records, this is routine work and I am happy to take it on. It is also genuinely doable yourself, and the diagnosis in step one costs nothing.
The uncomfortable part is not the fix. It is the arithmetic that comes before it. If your contact form has been dropping messages into junk this whole time, how many enquiries went in there before anyone thought to look?
Tags
Written by
Web Designer & Developer · Houston, TX
Founder of Ideavezy LLC and a full-stack developer with 20+ years of experience. He builds custom, mobile-first websites for salons, spas, restaurants, and local businesses across Houston and Texas — no templates, no page builders.
Book a free audit call