How to build a spam-free contact form without captchas

November 7th, 2009 By nFriedly

data_security_3Most anti-spam methods used by websites today are annoying at best. They use impossible-to-read captcha images, or they make users jump through some kind of hoop to get the email address instead of just clicking on it. This can mean lost sales and opportunities for you, because each hurdle turns away more users.

This article looks at how to use some simple HTML, CSS, & Javascript to protect your private information without making your guests jump through hoops.


Download a working copy of the contact form discussed here.

The Goal

I want users to be able to contact me simple and easy, no captchas, no math problems, just a regular contact form, clickable email address, and everything copy-paste-able.

The Problem

Spammers love captcha-free forms and clickable email addresses. (And lately, copy-paste-able phone numbers.) I do not want to receive a ton of spam!

The Solution

With a little bit of CSS and JavaScript wizardry, we can make a simple, easy-to-use contact page that will block almost all automated contact form spam.

Part 1: The Contact Form

We are going to make a standard contact form with one extra feature: an input named “url” and a note beside it that says “Don’t type anything here!”

The HTML:

<form method="post" action="/submit.php">
<p>Your name:
<br /><input name="name" /></p>

<p>Your email:
<br /><input name="email" /></p>

<p class="antispam">Leave this empty:
<br /><input name="url" /></p>

<textarea name="message"></textarea>

<input type="submit" value="Send" />
</form>

Then we use CSS to hide the input and the note.

The CSS:

.antispam { display:none;} 

Then we make a rule in the server that says ‘if the user typed anything in the “url” box, then throw it out.’

The PHP:

<?php

// if the url field is empty
if(isset($_POST['url']) && $_POST['url'] == ''){

	// then send the form to your email
	mail( 'you@yoursite.com', 'Contact Form', print_r($_POST,true) );
}

// otherwise, let the spammer think that they got their message through

?>

<h1>Thanks</h1>
<p>We'll get back to you as soon as possible</p>

A regular person won’t even see the box normally, and will therefore leave it blank without even thinking about it. If the CSS fails to load, they get a note explaining what to do.

However, when a spam bot looks at this, it sees a good spot to stick whatever spammy url they’re trying to advertise.

Now the php script on the server can tell who is a spammer and who isn’t. The regular people get sent to your email, the spammers get ignored!

Part 2:  Click-able Email Address

Spammers steal your email address by scanning through the source code of the site and grabbing anything that looks like an email address. So we’re going to make sure that there is no email address in the source code and instead generate it by JavaScript.

The Javascript:

var first = "yourname";
var last = "yoursite.com";

The HTML:

<p>My e-mail address:
<script type="text/javascript">
document.write('<a href="mailto:'+first + '@' + last+'">'+first + '@' + last+'<\/a>');
</script>
<noscript>
Please enable javascript or use my <a href="/contact.php">contact form</a>
</noscript>
</p>

A regular user will see a regular email address and things just work. A user who happens to have javascript disabled will see an explanation and an alternative solution. And a spammer won’t see a thing!

This method can easily be extended to phone numbers and other personal information.

Advanced version: Prettier message body and a proper From: field

These were the most commonly requested features, so I added an advanced version that changes the From: field of the email to whatever the user typed in the box, and removes all of the Array brackets from the body of the message:

<?php

// if the url field is empty
if(isset($_POST['url']) && $_POST['url'] == ''){

	// put your email address here
	$youremail = 'you@yoursite.com';
	
	// prepare a "pretty" version of the message
	$body = "This is the form that was just submitted:
	Name:  $_POST[name]
	E-Mail: $_POST[email]
	Message: $_POST[message]";
	
	// Use the submitters email if they supplied one 
	// (and it isn't trying to hack your form).
	// Otherwise send from your email address.
	if( $_POST['email'] && !preg_match( "/[\r\n]/", $_POST['email']) ) {
	  $headers = "From: $_POST[email]";
	} else {
	  $headers = "From: $youremail";
	}
	
	// finally, send the message
	mail($youremail, 'Contact Form', $body, $headers );

}

// otherwise, let the spammer think that they got their message through

?>

The preg_match() is there to make sure spammers can’t abuse your server by injecting extra fields (such as CC and BCC) into the header. Take a look at http://www.thesitewizard.com/php/protect-script-from-email-injection.shtml for more info.

Be sure to check the comments below for several other variations.

Complete Examples

A complete working copy of everything mentioned in this article, including both the simple and advanced versions, is available for download here: http://nfriedly.com/stuff/spam-free-contact.zip

For a live demo of all of this and more, see my contact page.

WordPress version

I found that there is an anti-spam plugin for WordPress that uses similar methods to the ones I describe here: http://wordpress.org/extend/plugins/nospamnx/ – I installed it on this blog and it’s stopped nearly 30,000 spam comments so far.

Does your website need help?

I am an Experienced Web Developer with a sharp eye for security. I can make your site easier to use while at the same time cutting down on level of spam you receive through it.  Contact me for more information and a free quote.

95 Responses to “How to build a spam-free contact form without captchas”

  1. Mike says:

    Is there a way to have the “Thanks, We’ll get back to you as soon as possible” message appear on the same page as the the form, instead of user being sent to submit.php? Ideally I’d like to replace the form inputs after hitting submit with the text “Thanks, We’ll get back to you as soon as possible”. I have a single page site, and it seems a bit unnecessary to send them to another page after submit with only one line of text on it.

  2. nFriedly says:

    Hi Mike, that is certainly do-able, but it’s a little beyond the scope of this project. There’s a number of jQuery libraries that help with what you want though – http://www.google.com/search?q=jquery+ajax+form

  3. mike s says:

    Mike,

    Perhaps this will work for you…

    function yeahThanks(){
    window.location.replace(“your-url-here”);
    }

    Am thinking of trying it out on my site.

    Cheers…

  4. [...] erst dann eine E-Mail an dich versendet, wenn das Feld mit name="spamschutz" LEER ist (hier eine Anleitung). Das bedeutet: Wenn ein Mensch dir eine E-Mail schreibt, ist dieses Feld auf jeden Fall LEER, weil [...]

  5. David says:

    Hi,

    I was looking for a solution for spamming and followed your very good instructions. It’s just a small point but for some reason when I tested the solution, if I use ‘ in the message such as. “I’m interested in your coming to the reunion….”. The generated e mail shows the following:

    I\’m interested in coming to the reunion…etc.

    This occurs whenever ‘ is used.

    Have you come across this \ addition before? I did a Google search and couldn’t find anything on this problem.

    Many thanks in advance.

    Great site!

    David

  6. nFriedly says:

    Hi David,

    You’re probably using an older version of PHP that has a pseudo-security feature called “magic quotes”. It was never actually very secure, so it was deprecated a little while back and it’s been removed from the current release of php. Try upgrading to the latest version or, if that’s not available, take a look here for info on disabling it: http://www.php.net/manual/en/security.magicquotes.disabling.php

  7. Ron says:

    This works great in IE and Firefox, but did not work in Crome. It went through the process without errors but never delivered the email. Tried it several times. Any idea why it doesn’t work in Crome?

    Thanks, Ron

  8. nFriedly says:

    I wouldn’t expect this, but it’s possible that Chrome’s AutoFill feature is putting some text into the hidden url field.. It’s the only thing I can think of.

    This page has info on how to disable the feature: https://support.google.com/chrome/bin/answer.py?hl=en&answer=142893

  9. Melissa says:

    Thank you so much for this! I am learning PHP and built our contact form thinking that everything was OK. Upon launch I had 30+ spam messages in 2 hours. But I think that by adding these features I can prevent that. Thank you again for this, fingers crossed!!

  10. Bryan Cannon says:

    Thank you for posting this solution. How would I add the Sender to be copied in on the email (so that they would have a copy of the information they submitted)?

  11. nFriedly says:

    Bryan, while the technical side of that isn’t very difficult, I would caution against it because it would open you up to abuse if a spammer ever noticed that they could put anyone’s email down and then have your server send them out a spam message.

    But, if you want to do it, you would just add a semicolon and then their email address to the “you@yoursite.com” part.

  12. Ken says:

    Thank you so much!
    As a user on the internet, I have found mail forms with impossible to read captcha’s a royal pain in the @**
    This form is just the thing!
    P.S. You can add a page redirect (since I made one) to a “thank you” page with…
    ex:
    $url = ‘thankyou.html’;
    echo ”; ?>

    (Note: must use JUST BEFORE ?> at end of script.
    Thanks,
    Ken

  13. Oskar says:

    This works great! I am using it for the mobile version of my webpage http://kragerofjorden.no ! Thank you!

  14. Ken says:

    Thanks! Form works great!

  15. Julian says:

    I need a anti-spam form on my site and this seemed to work really well- clever,user-friendly, and easy for a non-developer like me to implement.

    I did it yesterday and checked it in firefox. Received forms with no problem.

    Today I tried with ie and Chrome. Nothing doing. The speed at which the thanks page comes up suggests spamblocking. I suspect Ron is right, and some autofill is filling the box. Do you have any ideas please?

  16. Julian says:

    Hi Nathan,

    **It was the autocomplete in IE** bet it’s the same in Chrome. I turned it off and got the form. Interestingly I saw the confidence bar briefly fill up with green as it was sent,FYI – in testing if you don’t see this and get the Thanks page, you’ve been spamblocked.

    I changed the hidden “URL” to “monkeyuncle” on contact.htm and submit.php reasoning that if the autocomplete didn’t know what a field was it might not complete it, but this didn’t work.

    Any suggestions? This is such a user-friendly idea that I am reluctant to let it go. Best Julian

  17. Julian says:

    OOOPS!!!!!

    Big apologies, Nathan. The forms from ie and chrome went straight into junk and I didn’t see it. I humbly beg your forgiveness, you brilliant programmer!!!!

    I am going to a) have a nice cup of coffee

    b) implement this on my site

    c) of course I will keep in the credit. You deserve it. I am also going to recommend your site in my links page when I go live

  18. nFriedly says:

    Cool, glad to hear it’s working for you. I did a little digging and found that I can add a autocomplete="off" attribute to the form element, so I’ll probably release an updated version with that included sometime soon to remove any possibility of autocomplete causing people to be treated as spammers.

    Here’s more info if anyone’s interested: https://developer.mozilla.org/en-US/docs/Mozilla/How_to_Turn_Off_Form_Autocompletion & http://msdn.microsoft.com/en-us/library/ms533032%28VS.85%29.aspx

  19. Julian says:

    Good. Please put down some info on this comments page when you do and I will implement the update. You can see your form here: http://www.picture-research-courses.co.uk/costs.htm

  20. Julian says:

    Hi Nathan,

    I’ve put a link on my website here. Hope this meets with your approval. cheers Julian

    http://www.picture-research-courses.co.uk/links.htm

  21. nFriedly says:

    Will do. And your site looks great – thanks for the links!

  22. Yulia says:

    Great job Nathan! I just slightly modified this form and I’m going to implement it for my html contact page. I put your link to stumbleupon

  23. Grimnir says:

    I have implemented this same solution on my client’s websites with great success, they don’t get any spam anymore.

    To those of you who are worried that the spam-bots will be able to detect the hidden field:

    1. Give the hidden field an id that doesn’t give away what it does, like ‘country’, ‘name’ or something similar.

    2. Don’t set the css-property directly on the input-field. Instead create a CSS-class that has the display: none property. Again, give the CSS-class a name like ‘content-field’ or something like that. Most bots won’t go to the trouble to parse your stylesheet.

  24. Taylor says:

    Thanks, its really very helpful and I used on my website.

  25. Oregonerd says:

    Testing your code. Excellent thanks.

    I wanted to pass the first name of my guest to the submitted output to personalize. The following works nicely :)

    Thank you

    var sStr = “.”;

    document.write(sStr);

  26. There is a big down side to this in that you have not put anything in to stop screen readers from reading this: the CSS display:none would be ignored. So anyone with visual impairment cannot send you a message. This would be in violation of several guidelines to do with Web Accessibility.

  27. nFriedly says:

    Really? I haven’t personally tried it, but I wouldn’t expect a screen reader to not read display:none text. Even if it did though, it would read the “Leave this blank” description, which should be fairly straightforward to follow.

    Update: according to http://alistapart.com/article/fir#section4 out of 5 tested screen readers, only 1 reads display:none text. And for that case, I still believe that the description is sufficiently clear to not cause large issues.

    Updae #2: If you set both display:none & visibility:hidden on the text then it appears that all screen readers will skip it – http://juicystudio.com/article/screen-readers-display-none.php#comment3 So I’ll add that (and some &ltlabel> tags) the next time I update the article.

  28. kenneth says:

    hi,

    Tnx for such a nice contact form. Is there a way you can implement a form validation on this one? That would be perfect!

  29. nFriedly says:

    You absolutely could – it should work with any standard JavaScript form validation tool.

  30. Paul says:

    How would I add validation to every field in this form?

  31. nFriedly says:

    Hi Paul, that’s beyond the scope of this article, but like I said above, any standard JavaScript or PHP validation technique should work with this form. Try out this one for example: http://docs.jquery.com/Plugins/Validation

  32. Jenny says:

    I so wanted to use this

    $url = ‘thankyou.html’;
    echo ”; ?>

    so I added
    :
    // otherwise, let the spammer think that they got their message through
    # Redirect user to success page

    $url = ‘thankyou.html’;
    echo ”; ?>

    but it displays a few symbols and nothing.
    I must be missing something.
    thanks

  33. nFriedly says:

    Yea, I think there has to be some other PHP code on the page to make that work. But a redirect is simple enough, just do this:

    header('Location: http://www.mysite.com/thankyou.html');
    exit('Redirecting you to http://www.mysite.com/thankyou.html');

    And then make sure you don’t have any echo statements or any text or HTML outside of the <?php and ?> tags before it gets to those lines. (Everything after the exit line is ignored.)

  34. Kassie says:

    Love this script and want to replace Captcha on a client site but all my forms submit to an external web-hosted CRM so currenly posts the forms using:
    form action=”xxxxxxwebsite.com” method=”POST” onsubmit=”return Form1_Validator(this)” language=”JavaScript” name=”FForm1″

    and I can’t work out how to replace your ‘post to email’ php code with the post to url code

    Any ideas?

  35. nFriedly says:

    Hi Kassie. Unfortunately this technique will not work with an externally-hosted CRM unless you can convince the external host to implement it.

    You could host it on your own account as long as you can run php. (If you can’t run php, try out http://www.dreamhost.com/r.cgi?225072 – I have a few PHP sites there and I can probably get you a discount code if you’re interested.)

  36. Kassie says:

    Thanks for such a speedy response!

    It’s actually a WordPress site hosted on a Rackspace server, the forms are all on separate html pages, calling into the WP site pages using shortcodes and already processing the form post function through a separate formprocess.php for the Captcha so I was hoping I could just replace existing Captcha function with yours, sadly I know nothing about php!

    Would you be prepared (for a fee obviously) to take a look at it for me?

  37. nFriedly says:

    That might be possible, but it’s a little complex for this post. I’ll send you an email with some more details.

  38. Jon says:

    I loved the look and feel of your form. I downloaded and followed your instructions, however, when I hit submit, the form does not redirect, but rather, shows the submit.php file code instead. What am I doing wrong?

    Thanks,

    Jon

  39. nFriedly says:

    That probably means that your server does not support PHP. Here’s the instructions for installing it: http://www.php.net/manual/en/install.php – or you could try out a hosting company that comes with it pre-installed. I Mostly use Dreamhost for stuff like that – http://www.dreamhost.com/r.cgi?225072 If you’re interested, I can probably get you a discount code for them.

  40. Jon says:

    I use GoDaddy and am on a linux platform. They told me to use /gdform.php, so I assume it is supported. I am still new to all of this.

  41. Jon says:

    I got it to work. I forgot to post it live. Thank you for your help. :-)

  42. nFriedly says:

    I haven’t done anything with GoDaddy in ages, so I don’t think I can offer much help there. I’d say try working with their support staff a little more and if they can’t get their servers to correctly process PHP then you might need a new web host.

    Woop, posted that before I saw your followup. Glad to hear you got it working :)

  43. Jon says:

    Is there any way to make the fields required? BTW, your code and instructions were the simplest to follow. Thank you for making my life so easy. It took me 3 weeks and still didn’t figure it out, but with yours, it took me a few hours.

  44. nFriedly says:

    It’s definitely possible, but it’s a bit beyond the scope of what I want to do here. (I’m trying to keep this form simple.) Check out http://plugins.jquery.com/?s=form+validation for some good options though. I imagine at least one or two of those is going to be fairly straightforward.

  45. I have been surfing on-line more than three hours these days, but I never found any fascinating article like yours. It’s lovely value sufficient for me. In my opinion, if all site owners and bloggers made good content material as you probably did, the internet will probably be a lot more helpful than ever before.

Leave a Reply

 


RSS nFriedly Web Development » Technical Blog