Module 7 · 7.3
English
Mail from your website and forms
Module 7: Newsletters and mail from your website · Reading time: 11 minutes
Learning objective: After this lesson, you'll be able to set up website mail (contact forms, order confirmations, WordPress notices) so that it arrives, doesn't cause DMARC problems, and doesn't let your package be abused.
Key concepts: PHP mail(), SMTP with login, envelope sender, From vs. Reply-To, never using the visitor's address as From, SMTP plugin, transactional mail, form spam, captcha, honeypot, sending limit, local delivery when the MX points elsewhere
Your website is also a mail program
Your website sends more mail than you might think. A contact form emails you every message. A webshop sends order confirmations, invoices and shipping notices. WordPress emails you about a new comment, an update or a password reset. To the recipient, this is just mail from your domain, and it's checked just as strictly as mail from your mailbox: SPF, DKIM, DMARC, reputation.
Yet website mail goes wrong more often than regular mail. This is usually down to one of three things: the way it's sent, the sender address, or abuse of the form.
Method 1: PHP mail()
The simplest way for a website to send mail is the PHP function mail(). The website then hands the mail directly to the web server's mail program, without logging in. It usually works, but it has drawbacks:
- The envelope sender (Return-Path) often becomes your package's system account, such as
gebruiker@server.hosting.example, rather than your own address. SPF is then checked for the server's domain, so it isn't aligned with your From domain (lesson 7.1). - Whether a DKIM signature from your domain gets added depends on the server. Some servers do this, others don't.
- You see nothing back. If the mail doesn't arrive or you get a bounce, it goes to the system account, which nobody checks.
- No limit per form. If the form is abused, the server will send mail in your name until your package's limit is reached.
Method 2: SMTP with login (better)
Let your website send mail the way your mail program does: by logging in to the outgoing mail server with a real mailbox, via port 587 (STARTTLS) or 465 (SSL/TLS), using the full email address as the username (lessons 1.4 and 2.1). This way:
- the envelope sender is your own address and the mail is signed just like regular mail from your domain;
- bounces end up in a mailbox you can actually read;
- the sent mail appears, if you want it to, in the Sent folder;
- you can set your own sending limit per mailbox.
In WordPress, you do this with an SMTP plugin. It replaces WordPress's default mail function with sending via SMTP. You fill in the same details as in a mail program: server, port, security, username and password. For this, it's best to create a separate mailbox, for example website@example.com. If the password is ever stolen, your own mailbox stays safe, and you can see at a glance which mail comes from the website.
For a custom PHP site, there are libraries that send mail via SMTP, such as PHPMailer. Put the password in a configuration file outside the web folder, not in a page's code.
At WebOké, the SMTP details are in DirectAdmin guide 8.1, along with your package's server name; with other providers, they're in the guide or the welcome email, and it works the same way.
Lots of transactional mail? Use a service
If you send hundreds of order confirmations a day, a transactional service is worth it: a sending service for automated mail. It has a good reputation and shows you what happened to each message. You connect the domain just as you would with a newsletter service (DKIM via CNAME, lesson 7.1). Keep transactional mail and newsletters separate: a failed campaign shouldn't affect your order confirmation emails.
The golden rule: the visitor's address is never the sender
With a contact form, it's tempting to send the mail "from" the visitor. That way, you can click Reply directly in your mail program. It looks like this:
From: Pieter <pieter@example.net>
To: info@example.com
Subject: Vraag via het contactformulierBut your server isn't allowed to send mail on behalf of example.net at all. Suppose example.net has a DMARC policy of p=reject. The receiving server (yours, or Gmail's if you forward mail) sees a message with From example.net that doesn't come through example.net's SPF or DKIM, and rejects it. Many large mail domains have a strict policy: a check on 8 November 2026 found that yahoo.com, aol.com, ziggo.nl and kpnmail.nl, among others, had a DMARC policy of p=reject, and icloud.com, gmx.de and web.de had p=quarantine. A visitor with such an address fills in your form, and you never receive the message, or it ends up in your spam folder. Domains with p=none (such as gmail.com and hotmail.com at that time) can tighten their policy at any moment, and Google's guidelines explicitly state that you must not impersonate Gmail addresses as From. So never rely on it going well.
The correct way:
From: Website example.com <website@example.com>
Reply-To: Pieter <pieter@example.net>
To: info@example.com
Subject: Vraag via het contactformulierThe sender is your own address, so SPF, DKIM and DMARC are all correct. The visitor's address goes in Reply-To. If you click Reply, your answer goes to Pieter. In most form plugins, you'll find this in the mail settings: set a fixed address from your own domain as the Sender (From), and put the field where the visitor enters their address in Reply-To.
Protecting forms against abuse
An unprotected form will sooner or later be found by bots. At best, you get spam. At worst, it's abused to send spam to others: if your form sends a confirmation to "the address the visitor enters", a bot can enter any address in the world there, with its own text in the message. Then your domain ends up sending spam, with all the consequences for your reputation (lesson 5.2).
Protection in layers:
- Captcha or an invisible check (such as reCAPTCHA or Turnstile) stops most bots.
- Honeypot: a hidden field that people don't see but bots fill in. Filled in? Then the message isn't sent.
- No free text in a confirmation to the visitor, or no confirmation at all to the address entered. A simple message like "Thanks, we've received your message" on the page is enough.
- A sending limit per mailbox for the website mailbox, lower than your package's overall limit.
- Keep software up to date. Outdated plugins are a well-known way in for abuse.
Do you notice your package suddenly sending a lot of mail, or get a notice that the daily limit has been reached while you yourself send little mail? Change your passwords straight away and check which form or script is sending the mail (lesson 3.4).
Mail is elsewhere, website is here
A common pitfall: your website is with one provider, your mail with another (for example Microsoft 365 or Google Workspace). Your MX points to that other provider. But the web server still thinks it handles mail for your domain itself, because the domain is also set up on its own package. Mail from the form to info@example.com then gets delivered locally, into a mailbox on the web server that nobody checks, or it returns an error such as 550 ... mailbox unavailable.
The solution is to tell the web server's control panel that mail is handled elsewhere. In DirectAdmin at WebOké, that's the tickbox "Use this server to handle my emails" under MX Records (switch it off). Other panels have similar settings, such as "email routing: remote" in cPanel. Even better: have the website send mail via SMTP using a mailbox with the provider where your mail actually lives.
Summary
- Website mail is checked just as strictly as regular mail. Have the site send mail via SMTP with login, not via
mail(). - Use a separate mailbox for the website, with its own sending limit.
- The visitor's address belongs in Reply-To, never in From. Otherwise DMARC fails, and for domains with
p=reject(such as yahoo.com, ziggo.nl, kpnmail.nl) the mail gets rejected. - Protect forms with captcha, honeypot, and no free-text confirmations to addresses entered by visitors.
- If your mail is with another provider, switch off local delivery on the web server or send mail via SMTP with that provider.
Quiz
Exercise
Exercise O7.3: Reviewing website mail. Fill in a form on your own website using a Gmail or Outlook.com address as the sender, and look at the headers of the message that arrives for you. Answer the checklist: which address is in From, is the visitor's address in Reply-To, what do spf=, dkim= and dmarc= say under Authentication-Results, does your site send mail via SMTP or via mail(), and does the form have a captcha or honeypot? If you don't have your own website, answer the questions for the example in this lesson instead. You'll then receive the model answer.
Exercise O7.3: Reviewing website mail
You do this exercise yourself; no one checks it. Tick off what you've done and mark the exercise as done.
From = a fixed own address, Reply-To = the visitor, send via SMTP with login, captcha or honeypot, and no confirmation with free text (spammers abuse that). If the visitor's address is in From, DMARC fails as soon as the visitor's domain has a policy (lesson 7.3).
Your ticks and notes only stay in this browser; we don't send them anywhere. If you're signed in, we only save in My courses that you've done the exercise.
Further reading
The steps in these articles apply to WebOké's control panels; with other providers, it works in a similar way.
- A contact form on your website: have fields emailed and prevent spam
- How do I get the correct sender with mail() in PHP? SMTP
- Sending email via PHP fails, MX records point elsewhere
- Limit for sending mail
- Daily email limit reached
- DirectAdmin guide 8.1: All settings at a glance: servers, ports and security
- DirectAdmin guide 5.10: E-mail with Microsoft 365 or Google Workspace, website with WebOké
