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 not get you hardly any spam!

A downloadable example is provided at the end of the article.

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.

Complete Examples

A complete working copy of everything mentioned in this article is available here: http://nfriedly.com/stuff/spam-free-contact.zip

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

Update: 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/

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.

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

  1. Noah Glaser says:

    I used to get spam all over both my forums. I finally solved that problem the other day. I knew how to but was always too lazy to do anything about it. But problem solved as of now. No more waking up in the morning with incest this and barnyard that posted all over.

  2. nFriedly says:

    LOL, now I’m going to show up in google for “incest” while you’re getting juice for your site. Thanks Noah.

    But seriously, what did you do to solve the spam problem? I doubt it was this..

  3. Decoratiing Ideas says:

    lol Install analytics and let me know if you get shown for incest ever once. If so I would die laughing.

    But I just did one of those questions that people have to answer. The one I am using now is

    “What is the year minus one”
    so it would be 2008.

    I have not had a spambot since.

  4. nFriedly says:

    haha, I have google analytics installed + analog on the backend, I’ll let you know if I get any hits.

    The year minus one is better than a captcha, but it’s still extra work for the users. That’s what I’m trying to avoid with this method.

    I’ve only had a single comment spam from my contact form, and after looking through the logs, i realized that a real person came and typed in some marketing message and hit submit. You can’t really protect against that…

  5. E3 Coverage says:

    Now that Ive said that I can see you going and posting incest on my site lol.

    And yes the typo was meant in the above post as for some reason i get traffic for that in google. I am going to optimize for typos ;)

  6. Gaming Reviews says:

    I may use your idea for a few of my contact forms. I get spam still from those now and again. But normally its not even properly done spam. Like somebody has a retarded spam bot that doesnt even post the link and spam right so I just get an email that is all broken.

  7. nFriedly says:

    LOL, yea, I used to see those all the time.

    No, I’m not going to post dirty pics on your site, but I might post half a dozen web dev keywords all linked back to my site :P

  8. Justin Long says:

    Great post, I have added a similar feature on my site for the contact form and was looking at doing something similar for the comments.

  9. nFriedly says:

    Thanks man, glad to know somebody is finding it useful!

  10. These are relatively weak defenses against spam. I use a timer to see if the form was submitted in less than a few seconds (good for defeating bots), a randomly named field with a random value, validation of all input fields (e.g. email address, phone #, etc.), and I also check to see if there are more than a few links in the submission.

  11. Justin Long says:

    I never thought of a timer to detect form submission time. On my comments I validate name, email address along with using a hidden field as the article above describes. Where the name and email will result in a focus on the improperly imputed field unless the hidden field does not valudate because of text being entered in the hidden field. If thy does happen it thanks them for their contribution and deletes their comment. I have considered checking to see how many links are posted in the comments but right now I am not having any problems and I want to keep it as light as possible

  12. nFriedly says:

    Joseph, that’s a fair argument, this article was going for simple and effective against generic spam bots. Anything targeted to even a specific piece of software (such as wordpress, which this blog runs on) needs more defense.

    On this blog I use Akismet and it has had a 100% accuracy rate so far.
    http://akismet.com/

    On an different blog I worked on in the past, I used Akismet and Spam Karma 2. That uses all of the methods you mentioned and more to determine if a comment was from a spammer.
    http://unknowngenius.com/blog/wordpress/spam-karma/

  13. Ivan Bayross says:

    Thank you for a really simple explanation and codespec.

    In its simplicity its brilliant and effective. I’m going to implement this approach with the few web forms I use.

    Ivan Bayross

  14. nFriedly says:

    Thanks Ivan, post a link here when you do! BTW, I love the look of your site.

  15. The anti-spam extra field is good.

    The Javascript to “hide” your email is only going to be partially successful.

    SpiderMonkey will cheerfully run the JS for the slightly smart spammers who run that to gather more/better emails.

    So you’ve only stopped the stupidest spammers, not the slightly smart one.s

  16. nFriedly says:

    Oh, certainly, this is just a basic line of defense. The smart ones will still get you, but let’s face it: most spammers aren’t that smart or they’d be in a different line of work.

  17. Ryan M. says:

    Not sure if you have taken a look at JQuery. At this point in time I’m sure you have. When i first saw i ran as fast as i code. The big chunks of code were over whelming. Besides all that, Jqery has a write in code snippet that can be used to plug chunks of code anywhere on the page based on criteria. This link ( http://docs.jquery.com/Manipulation ) has the code calls to manipulate code on a page. Very helpful if added on to your tut to provide reusable code and added security to perhaps dynamically create the whole form.

  18. nFriedly says:

    Oh yea, I love jQuery, if you take a look at the live demo linked in the article, http://nfriedly.com/contact , you’ll see that it uses jquery to go a little above and beyond what I mention here.

  19. William says:

    The method that you described does sound very interesting. However, recently I started using Mollom ( http://mollom.com ). There is no captcha. The form is processed by their bot and if it appears to be spam, it is rejected. I was getting at least one spam message in my inbox everyday even with a captcha. I started using Mollom about a month ago and so far only 1 spam message has gotten through.
    I do want to look at the method that you described above though. I definitely see some benefits with it.

  20. Naz says:

    Hey, I tried this code and it works fine. But when I get the email. It comes with Array{} codes and even it shows the submit. Is there a way I can make it much better? Also it comes from my mail server which is an ugly address lol.

  21. nFriedly says:

    Hi Naz, you can do that by changing the line

    mail( 'you@yoursite.com', 'Contact Form', print_r($_POST,true) );
    

    to this:

    $youremail = 'you@yoursite.com';
    
    $body = "This is the form that was just submitted:
    Name:  $_POST[name]
    E-Mail: $_POST[email]
    Message: $_POST[message]";
    
    if( $_POST['email'] && !preg_match( "/[\r\n]/", $_POST['email']) ) {
      $headers = "From: $_POST[email]";
    } else {
      $headers = "From: $youremail";
    }
    
    mail($youremail, 'Contact Form', $body, $headers );
    

    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.

  22. Alicia says:

    Yes, checking for extra fields injection is mandatory.

    One migh also want the Name to be added there too ( not just the email ), and the subject of the email can be something like ‘Contact Form ‘ . $POST[subject] ..

    I use formchamp.com to build and generate forms that send emails, and they also have a captcha less version that uses a hidden field to catch spam. Just as per suggestion of this blog entry

  23. Clay says:

    Great idea but why does my e-mail address show up below the contact form.. if I rem out that section the form does not seem to work… this is totally making this whole thing useless to me.

    Please let me know why this is happening.

  24. Andrew says:

    I like this idea, that will help make capturing spam much easier. We sometimes switch the form names. We tell the user to enter their name, but we call the input field “email”.

    Enter Name: [input name="email" /]

    Tons of bots will enter an email address, and since you did not ask the user for “email” you have successfully trapped a lot of form spam as well. It is a very similar method to what you are doing. Of course our backend code knows this is switched and we use the form variable “email” for the person’s name.

  25. Andrew says:

    And one other thing you can do… Count how many keystrokes it takes to fill out the text using JavaScript. If the user does 11 keystrokes but you have 1000 characters – it is likely spam as well. (Or sometimes it’s a copy and paste) – but still another tool to use to cut out the junk. Since legitimate users do this, you probably flag all of these comments as “potential junk”.

  26. nFriedly says:

    Haha @ switching the names – that’s clever!

    I like the keystrokes idea, but for me personally, I don’t get enough junk to warrant a third “potential junk” category.

  27. paul says:

    i’ve also had really good results using that contact form. good write up :)

  28. Ivan says:

    I use validation for common words and injection (and autoban) and I have catch quite a few :)

  29. Ivan says:

    I use validation for common words and injection (and autoban) and I have catch quite a few :)

    addendum:
    I just commented and I have a message “duplicate comment”. That may be true as my mouse button has problem but it also show a very unprofessional system as the submit button shoud be disable after clicking on it, as I do in my form.

  30. Marty says:

    I was just wondering (actually hoping) if by chance anyone has implemented the form by placing the javascript in a separate js file and, if so, provide the revised html for the ‘My Email Address’? Thank you ahead of time.

  31. nFriedly says:

    Hi Marty, to use an external javascript file, change the <script> tag to have src="path/to/javascript/file.js" and then cut all of the code between the opening and closing <script> tags and paste it into the new javascript file you made.

  32. jo says:

    You may also name the file “file.php”, that allow you to use php code inside :)

  33. Eldin says:

    Thanks man, this is great, Im using it right now at my website.

    I tried many other php scripts with captcha, but this is best one, really..

    thanks again dude

  34. [...] Contact information: any phone number or email address posted here will be picked up by spammers. A basic solution could be a .jpg on website with your phone number and email. A better solution could be a contact form like this one: How to build a spam-free contact form without captchas [...]

  35. Zack says:

    I downloaded the script and will test it.

    Thank you, thank you, thank you! I dislike captchas and even if a little spam gets through but most is caught I’ll be happy.

    Now, question is can someone make it into a Joomla component? It would be very popular I’m sure.

    Z.

  36. Ryan Casey says:

    Hi mate, thanks for the code, I just have one problem. It still sends me an email even when the form is completely empty. Is there an easy way to make two or 3 fields required before it sends.

    I love the simplicity of it though.

  37. nFriedly says:

    Hi Ryan,

    That should be pretty easy to do, in the PHP code, you’d want to add an extra “if” statement around the mail() call, for example:

    <?php
    
    // if the url field is empty
    if(isset($_POST['url']) && $_POST['url'] == ''){
    
    	if($_POST["message"] == "" || $_POST["name"] == ""){
    		echo "<p>Please press the back button and fill in all fields</p>";
    	} else {
    		// 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>
    

    And then you could go into that if statement and add in any other fields you wanted to make required. Just add a “||” (two pipes, not the letters I or L), between each field you want to be required.

  38. yellaflow says:

    Thanks a lot for this contact form. However, when I add the extra lines of code from the previous comment to make some fields required, I get the following error:

    Parse error: syntax error, unexpected $end on line 93

    Line 93 being the closing html tag. I know zero about php so I really can’t figure out what I’m doing wrong.

  39. nFriedly says:

    That sounds like it’s missing a closing ) or }. Did you add all four new lines (6, 7, 8, and 11 in my previous comment)?

  40. yellaflow says:

    That’s it – I missed the closing } on line 11. I got it working now; thanks again and sorry for the n00b question.

  41. nFriedly says:

    Awesome, glad it’s working for you!

  42. sky says:

    I was just wondering how to get the email address changed to a different email address on the received email. The only reason I am asking is because I am using an addon domain. I do not want it to show my business email address on a hobby addon domain website I am using this form for.

    How do I work around that?

  43. nFriedly says:

    Hi sky,

    If you have a second personal email address, you should be able to just edit the code to show that one instead.

    If you don’t have a personal email address, you could sign up for a free gmail account and either use it for your personal emails or have it forward all incoming emails to your work address.

    Does that answer your question?

  44. sky says:

    Hmmm I dont know. I dont think so? :) The hosting I have allows you to add four domains to the one you sign up with for hosting. So my main one is for work, and if you go to blahblah.com and blah22blah.com they are all sharing the hosting. It is like getting 5 domains hosted for the price of one. However, I think this might be something *they* have to fiddle with ?

    This is my first time to use add on domains and alls I gotta say is ((annoying)). Installing Typo3 on it was a nightmare !

    Ok I added the yahoo email and it still is showing the mail server main root domain … The first one I signed up with seems to be the ‘hub’ site.

    I havent found any decent tutorials or info regarding add on domains. Everything so far is just trial and error. Your form is the only one I have been able to get to work properly on any add on domain – minus the annoying rootsite.hostingserveraddress.com email FROM addy …. to work!

    So yay to you for that ha!

  45. sky says:

    I need to add a From field into the script. Could you please show me what and where to place that?

    Im not a script techie – by a long shot! :)

    I do so appreciate your help.

  46. nFriedly says:

    I see, try this. Change the line that reads:

    mail( 'you@yoursite.com', 'Contact Form', print_r($_POST,true) );
    

    to this if you just want to have the From field show your email address:

    $youremail = 'you@yoursite.com';
    
    mail( $youremail, 'Contact Form', print_r($_POST,true), "From: $youremail" );
    

    or this if you want it to have the submitters email address in the From field:

    $youremail = 'you@yoursite.com';
    
    if( $_POST['email'] && !preg_match( "/[\r\n]/", $_POST['email']) ) {
      $headers = "From: $_POST[email]";
    } else {
      $headers = "From: $youremail";
    }
    
    mail( $youremail, 'Contact Form', print_r($_POST,true), $headers );
    

    (It would then default back to yours if they do not enter an email address.)

    See this section of the comments for a little more details: http://nfriedly.com/techblog/2009/11/how-to-build-a-spam-free-contact-forms-without-captchas/comment-page-1/#comment-2113

  47. sky says:

    Hi there again :) I must tell you that works! Thanks so much for that. I tried fiddling with the removal of the arrays (mentioned earlier in someones comment) and keeping the from field, but I just fouled it up. I will check back to see if perchance you add something to that effect *S*

    Thank you again – *Yay*

  48. nFriedly says:

    Awesome :D

    The other comment should work if you start from a fresh copy of the script and then do it, but it may not if you’ve made any other changes.

    I just left the Array() stuff in my personal one though, it doesn’t really bother me.

  49. Peter says:

    This is noce solution, but according to my expirience, if someone decide to be have "personal" aproach to your site (especialy if that is forum or something) they will easy find a way to automate buypassing this kind of protection. During a years I coded tens of diferent ways to protect contact forms on various sites and blogs, and at the end started to use hosted contact forms, so I receive all my response from many places into single inbox which is also hosted on their servers. Just mine 2 cents…

  50. Galaga says:

    I have to agree with Peter. This protection is not so hard to bypass but I like your out-of-the-box thinking.

Leave a Reply

 


RSS nFriedly Web Development » Technical Blog