Skip to content
Koltrix

Contact forms that send email: preventing spam and abuse

A contact form is an email-sending endpoint open to the internet. How attackers abuse it, and the checks that stop spam, header injection and reputation damage.

Koltrix Team5 min read
Black maze walls seen from above
Photo by Mitchell Luo on Unsplash
On this page(11 sections)
  1. What goes wrong
  2. Rule 1: fix the recipient on the server
  3. Rule 2: send from your own address, and use Reply-To
  4. Rule 3: clean every value that touches a header
  5. Rule 4: slow down the bots
  6. Rule 5: be careful with confirmations
  7. Rule 6: keep the form off the main sending path
  8. Monitor and alert
  9. Privacy and retention
  10. Testing checklist
  11. Key takeaways

A contact form looks harmless: a name, an email address, a message and a button. Underneath, it is an endpoint on the public internet that makes your server send email. Anyone can call it, as often as they like, with whatever text they want.

Attackers know this. A badly built form becomes a free spam relay, a way to send phishing from your domain or a path for injecting extra recipients into your messages. The cost is not only a flood of junk in your inbox. It is your sender reputation, which affects whether password resets and receipts reach real customers.

This guide covers the risks and the practical defences, in the order that gives the most protection for the least effort.

What goes wrong

Spam to you. Bots fill in forms with links and sales pitches. This is the most common problem and the least harmful.

Spam through you. If the form lets the submitter control who receives the message, your server becomes an open relay. A bot can use it to send thousands of messages from your domain.

Header injection. If you put form fields into email headers without cleaning them, a submitter can add line breaks and extra headers: a second recipient, a different subject or a hidden copy. A name field that contains Ada\nBcc: [email protected] can add a recipient.

Backscatter and harassment. If the form sends an automatic confirmation to the address the submitter typed, anyone can make you send mail to any address. Attackers use that to flood a victim with messages that appear to come from your company.

Reputation damage. Complaints and blocklisting from abusive sends hurt all your mail. See spam traps explained and the spam complaint rate threshold.

Rule 1: fix the recipient on the server

The recipient of a contact form message should be set by your code, never by the browser.

  • Hard-code the destination, or choose it from a short list on the server (for example "sales" or "support").
  • Do not accept a to field from the client, even a hidden one.
  • If the form needs routing, map a category value to an address on the server and ignore anything unknown.

This single rule removes the open-relay risk.

Rule 2: send from your own address, and use Reply-To

Do not put the visitor's email address in the From header. Mail "from" [email protected] sent by your server will fail authentication checks, because your server is not allowed to send for that domain. See From, Reply-To and the envelope sender.

The correct pattern is:

  • From: an address on your own authenticated domain, such as [email protected].
  • Reply-To: the visitor's address, so a person replying reaches them.
  • Subject: a fixed prefix, plus a cleaned version of any visitor text.

Rule 3: clean every value that touches a header

  • Reject or strip carriage returns and line feeds from any field that goes into a header: name, subject, email.
  • Use your email library's functions to set headers, which should refuse line breaks, instead of building the message by string concatenation.
  • Validate the address format, with a modest length limit. See signup email validation.
  • Escape the message body before inserting it into HTML. Prefer a plain-text email for form submissions.

Rule 4: slow down the bots

Combine a few cheap measures.

  • Rate limit by IP address and by a session or cookie, for example a handful of submissions per hour. Return a clear error when exceeded. See handling 429 and rate limits for the client side of the same idea.
  • A honeypot field. Add a field hidden from people with CSS and give it an attractive name such as website. Bots fill it in and humans do not. Drop those submissions silently.
  • A timing check. A form submitted within a second of loading was filled in by a program. Store a signed timestamp in the form and compare.
  • A challenge. If bots still get through, add a CAPTCHA or an invisible challenge service. Choose one that works for people with disabilities, and test it on a phone.
  • Block obvious abuse. Reject messages with many links, known spam phrases or oversized bodies.

Start with the cheap ones. Add a challenge only if the problem justifies the friction.

Rule 5: be careful with confirmations

An automatic "Thanks, we got your message" email to the submitter is friendly, and it is also the easiest way to be used as a backscatter cannon.

  • Prefer showing a confirmation on the page instead of sending an email.
  • If you send one, send it only after the bot checks pass, limit it to one per address per day and include the text of nothing the visitor wrote.
  • Never include a link or content that a visitor controls.
  • Do not send confirmations to addresses that bounced before. See hard vs soft bounces.

Rule 6: keep the form off the main sending path

Give form mail its own sending identity.

  • Use a separate address, and ideally a separate subdomain or API key, so that abuse cannot damage your product mail. See a sending subdomain strategy.
  • Use an API key with the narrowest scope, and keep it out of the browser. See email API key security.
  • Set low daily limits for the form's sender, so abuse hits a ceiling.
  • Queue messages and handle failures asynchronously, so a mail outage does not lose submissions or slow the page.

Monitor and alert

  • Count submissions per hour and alert on spikes.
  • Log accepted and rejected submissions with the reason, not the full content.
  • Watch bounces and complaints for the form's sender.
  • Review a sample of what arrives. New bot patterns show up there first.

Privacy and retention

A form stores personal data: names, addresses and whatever people type. Say how you use it, keep it only as long as needed and delete old submissions. Do not log message bodies in places that many people can read.

Testing checklist

  • The recipient cannot be changed from the client
  • A name containing a line break and Bcc: does not add a header
  • The visitor's address is in Reply-To, not From
  • A hundred rapid submissions are rate limited
  • A filled honeypot is dropped
  • Confirmation emails cannot be used against a third party
  • Alerts fire on a spike

Key takeaways

  • A contact form is an internet-facing email endpoint and needs the same care as an API.
  • Fix the recipient on the server, send from your own domain and put the visitor in Reply-To.
  • Strip line breaks from anything that enters a header.
  • Use rate limits, a honeypot and timing checks before a CAPTCHA.
  • Be cautious with confirmations and separate form mail from your product's sending.

Start with Koltrix

Your domain, one inbox, and an API that sends.

A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.

SharePost on XLinkedIn