logo
Page Affairs

Editing and Web Design

Contact Form Honeypots

If you have a contact form on your website, you will end up receiving a lot of spam unless you take steps to prevent it. Most of this spam comes from “spam ‘bots”—or automated programs that prowl the web looking for contact forms and submitting spam through them.

The problem with some protection methods is that they make like extremely difficult for honest people, which is a real downer. The worst option, in my view, is the CAPTCHA field—those horrid things that present you with contorted lettering that you can never decipher. In my view, making life harder for your legitimate site visitors is a bad move.

Honeypots

A nicer option is known as a “honeypot”—a less obtrusive option that trips up the bots while not troubling your visitors. Some honeypots present all users with a question, such as “what is 2 + 2”. If the answer “4” is not submitted, then the form is not sent. This is an easy enough question for a human, but a bot may have no idea what to put there (as they often just fill inputs with spammy nonsense).

Still, asking a silly question like that is an imposition on your users. So another option is to use this option but to hide the input with CSS (say, by positioning the input off screen or by using display: none;). The bot will see the input and fill it in, but most users won’t see the input. Rather than ask a question, you could set it up so that if anything is submitted into that field, the form aborts. The only thing to watch for here is that people using screen readers (or those with CSS off) may see this input, so you need to warn them by saying—“Leave this blank!” or similar. Still, it’s a bit of a clunky process.

Using a Honeypot Timer

A much nicer and less obtrusive kind of honeypot is a timer. This is a hidden field that records the time the page was loaded and then compares that time with the time the form was submitted.

A bot will normally submit a form within a few seconds, while a legitimate user will need to spend a lot more time reading the labels and typing answers. So the timer field can be set up to abort the form if it is submitted within a few seconds. That way, bots are caught out, but legitimate users are none the wiser—never troubled by silly questions or those hateful CAPTCHA fields.

A Code Example

I first saw this timer method suggested in the SitePoint Forums by felgall. I’m no PHP expert, but I wanted to figure out how to do this, and here’s what I came up with. Firstly, here’s the hidden code I include in the form itself:

<input type="hidden" name="loadtime" value="<?php echo time(); ?>">

Then, in the PHP that processes the form, I have this:

$loadtime = $_POST["loadtime"];

$totaltime = time() - $loadtime;

if($totaltime < 7) {
    echo("Please fill in the form before submitting!");
    exit;
}

So, if the page has been open for fewer than 7 seconds when the form is submitted, it aborts. It’s unlikely that this will catch out legitimate users, but it should give the bots a run for their money.

You can see an example of this in action here.

Regular Honeypot

In some scenarios, you might not be able to use the timer method—such as when your form processing script is in a file external to the page on which the form appears. In that case, you can use a regular honeypot.

Here, I will place a field in the form that requires a specific answer:

<div class="hide">
    <label for="spam">What is two plus two?</label>
    <input name="spam" type="text" size="4" id="spam">
</div>

I’ve wrapped the input/label pair in a div for styling purposes, and asked a pretty simple question. Firstly, via CSS I’ll hide that pair:

.hide {display: none;}

I prefer to use display: none than to position the content off screen, as assistive devices are less likely to see it if set not to display (so I believe). I’d prefer that people using screen readers etc. not be troubled by this field, but in case they do see it, the label explains what to do.

In the PHP that processes the form, I set the form to abort to abort if it contains any content that is other than “4” or “four”:

$spa = $_POST["spam"];

if (!empty($spa) && !($spa == "4" || strtolower($spa) == "four")) {
    echo "You failed the bot test!";
    exit ();
}

So there are two options for tripping up bots that won’t leave your site visitors frustrated and hating you.

Legacy Comments

Alex Kiefer — January 05, 2013

Maybe I’m missing something… but for the “Regular Honeypot” method, it looks like you’re hiding the entire div that contains the question “what is two plus two?” ..as well as the field the user is supposed to enter the “4” in.  So this field would always be empty (unless filled out by a spambot).
Shouldn’t the PHP script allow “4”, “four” and a blank field?

Ralph Mason — January 12, 2013

Hi Alex. Yes, the entire div is hidden, so as not to trouble most visitors. The field passes muster if it is either blank, or if it contains “4” or “four”. (The PHP “if” rule has two conditions: if the field is NOT blank, AND if the content is not 4 or “four”. So it’s fine for the field to be left blank—which will be what happens for most users, who don’t even see the spam field.)

Only those using something like a screen reader will see it, or those who have CSS disabled. In their case, you can either tell them to leave the field blank, or set it up so that they can just enter something simple—the approach taken here. (Apparently there can be issues with telling someone to do nothing.)

Bots will also see the field, and they, by their very nature, fill in form fields. The chances of them entering “4” or “four” are pretty slim. Entering anything else will trip them up. Does that make sense?

KG — March 14, 2013

If a spam BOT just submits to the POST url of the form on the page, and does not really fill out the form, how is this useful?

KG — March 14, 2013

When it posts the loadtime, what does that even mean? Can it be translated into something meaningful?

loadtime 1363214288

what in the heck would 1363214288 even mean?

KG — March 14, 2013

You say “In some scenarios, you might not be able to use the timer method—such as when your form processing script is in a file external to the page on which the form appears.” But in fact, you can certainly use the Honeypot Timer in circumstances where the form processing script is in a file external to the page on which the form appears. I know, I did it. So, that might not be the best example for readers to read as to why you might use one or the other as it might make one think you cannot use the Honeypot Timer when the form processing script is in an external file (e.g., submit form to formprocessor.php) and not in the form page itself (submit form to the form page itself to process).... when you could.

Ralph Mason — March 14, 2013

Thanks for your comments, KG.

If a spam BOT just submits to the POST url of the form on the page, and does not really fill out the form, how is this useful?

Doesn’t sound like it is useful at all.

what in the heck would 1363214288 even mean?

As I understand it, it’s the number of seconds since January 1st, 1970:

Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT). —PHP Manual

It’s a handy way to get a number for doing simple math calculations. Because it works in the background, it doesn’t matter in this situation what the number actually means. You’d have to look deeper into PHP to work out how to translate that number, or to render it as a date. It’s not my area, I admit.

Regarding using the timer in an external file, I’m glad to hear you got it working. I didn’t have much luck, but that’s not saying much. PHP is not my thing. Feel free to post a link to an explanation of how it’s done.

lucid — October 13, 2013

KG — March 14, 2013
If a spam BOT just submits to the POST url of the form on the page, and does not really fill out the form, how is this useful?

Why would a bot NOT fill out the form? What would be the point of a spambot simply POSTing nothing to a server? The point is for the bot to spread information, not to just hit POST urls with empty request strings.

John — October 29, 2014

So, not being code savvy, how SPECIFICALLY do I implement these three (regular honeypot) elements within the script?  Do the three elements go in a particular order and a particular location in the script or do they work no matter where in the script they’re placed?  Sorry, but I need a bit of a walk-through so I don’t mess up my page!  Thanks!

Ralph Mason — October 29, 2014

Hi John. Normally the location within the script wouldn’t matter, but how to use them depends a bit on the script you are using. I’ve offered one on this site that includes them. Perhaps give an example of your script and form code so that further tips can be given.

Nillervision — February 27, 2015

You should probably use strtolower($spa) before comparing it to “four”. Otherwise your script will reject inputs like “Four” or “FOUR”.

So the entire if statement should look like this:

if (!empty($spa) && !($spa == "4" || strtolower($spa) == "four")) {
echo "You failed the bot test!";
exit ();
}
Ralph Mason — February 27, 2015

Great point, Nillervision. Updated! I’m quite embarrassed that I didn’t do it that way in the first place. Anyhow, thanks for posting your improvement.

Eric Sexton — May 19, 2015

Hi Ralph,

Thanks for this post - really helpful. I found the example site useful also. Would it be possible to send on the php code you use for this form?

thanks,
Eric

Ralph Mason — May 19, 2015

Hi Eric. I actually have another post where I provide the full contact for code, right here: http://pageaffairs.com/notebook/php-contact-form. The ‘internal’ version includes the honeypot mentioned in this post.

Hope that helps!

Eric Sexton — May 20, 2015

Hey Ralph,

Thanks very much for this, it’s working well for me except one issue;

In the php file process.php, when I try to add the link to my style sheet in the head tags—
<link href=“css/style.css” rel=“stylesheet” type=“text/css” media=“all” />—it seems to break the process form. Any ideas how you can add a link to a style sheet in your process.php file example?

Thanks,
Eric

Ralph Mason — May 20, 2015

Hi Eric. It depends on where you put that line. Make sure to put it inside the head that is echoed in the PHP, where the <style></style> tags currently are. For example:

echo '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
<link href="css/style.css" rel="stylesheet" type="text/css" media="all">

© Page Affairs, 2008–2024