Introduction
You’ve just developed a contact form on your web pages to receive emails from internet users, and just a few days later, your mailbox is flooded with spams.
There is the tool Captcha which asks the user to type the letters and numbers of an image, this method ensures it is not a spam robot :
However, with these images, you often wonder if it is an uppercase or a lowercase character, if a character is placed before or after another one, and you need sometimes to zoom in for some characters (7 or z ?). So basically you don’t want to setup the captcha technology, especially when thinking of blind or partially sighted people.
Yes ! An anti spam method without using Captcha exists. This method is very easy and only uses the basics : i.e. HTML and Javascript. Since its implementation here, no more spams ! coming from the contact form.
Original code source of the form
The original HTML 5 code for the contact form is very basic.
<form id="formcontact" method="post" accept-charset="UTF-8">
<label>Subject</label>
<input name="subject" required="true" placeholder="Subject...">
<label>E-mail</label>
<input name="email" required="true"
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$"
placeholder="name@domain.com">
<label>Message</label>
<textarea id="message" name="message"
placeholder="Your message" required="true">
</textarea>
<cite>* Mandatory fields</cite>
<input name="sendmail" required="true" id="sendmail"
type="submit" value="Send">
</form>
Anti spam code
Obviously, do not use the attribute action="mailto:name@domain.com"
in the form.
This attribute attracts instantly spam robots.
A spam robot mostly fills all fields in a form. So in the anti spam method, a field, hidden to the user, is added to the form. This field will contain tricks that will invite the spam robots to fill it. When data coming from the form are managed, if the field is filled, then it is a spam, a spam robot filled it.
In the HTML code of the form, add the "ghost" field (here remark
) :
<label class="remark">Remark</label>
<input class="remark" name="remark"
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$"
placeholder="name@domain.com">
- set the attributes
name
and/orid
, and especially the attributespattern
andplaceholder
, these two last attributes are specially designed for end users, so the spam robots will surely try to fill the field when encountering these attributes. Hard to say if the attributepattern
has a benefit against the spams, but the most technically advanced spam robots may surely handle properly regular expressions. - add a tag
label
before the fieldinput
, it is one more clue for the spam robots that it is a real field for end users. - about the attributes
name
andid
of the field, do not use easy identifiers that could help spam robots to identify that the field is not a real one and used for anti spam purposes, for example :hidden, antispam, cache, fantome, ghost
… Use regular identifiers for forms :email%, remark, description
… we must suggest to the spam robots to fill the field. - Do not use the attribute
type="hidden"
to hide the field and the label to the end users, spam robots now know well why this attribute may be defined against them. To hide the field and the label, use the cascading style sheet. The classremark
in the css file sets thedisplay
property tonone
. Try to avoid coding this style property in the page body withinstyle
tags, it could be parsed and recognized by spam robots like the attributetype="hidden"
.
#formcontact > .remark { display:none; }
That’s all , PHP or your preferred language now handles : if the field remark
contains data, it is a spam, form data are rejected.
if ($_POST['remark'] != "") { die(); }
The PHP code can be enhanced if we want to log spams before the die
command, $_POST
variables are saved in a log file
using var_export
and file_put_contents
:
if ($_POST['remark'] != "") {
$msg = strftime('%Y-%m-%d %H:%M:%S')."\n";
$msg .= var_export($_POST,true)."\n";
file_put_contents('../logs/mailspams.log', $msg, FILE_APPEND | LOCK_EX);
die();
}
Anti spam strengthening with Javascript
Javascript strengthens the struggle against the spams. Why ? Pages volume is huge on the web, so most of the spam robots only read the HTTP
response
searching for forms with fields to be filled and submit
buttons.
HTTP response example
HTTP/1.1 200 OK Set-Cookie: 60gpBAK=R1224190331; path=/; expires=Tue, 20-Sep-2016 18:30:38 GMT Date: Tue, 20 Sep 2016 17:09:54 GMT Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Set-Cookie: 60gp=R446942833; path=/; expires=Tue, 20-Sep-2016 18:20:59 GMT Server: Apache X-Powered-By: PHP/7.0.8 Cache-Control: max-age=0 Expires: Tue, 20 Sep 2016 17:09:54 GMT Vary: Accept-Encoding
<!DOCTYPE html> <html lang="en"> … <body> <form id="formcontact" method="POST" accept-charset="UTF-8"> … <input name="sendmail" required="true" id="sendmail" type="submit" value="Send"> </form> </body> … </html>
Dynamic creation of the form after loading the page
When the form is built dynamically with Javascript in the DOM tree, so after the HTTP response, spam robots have less chances to detect the form except for the most advanced. The code for creating the form is heavier obviously, but almost all the spam robots have fled when the code is executed.
In this example, the form is dynamically inserted in this example at the end of the tag article
which is unique in the page, but
the form can be inserted after or before any element, depending on the architecture of your web site.
<article>
…
<form id="formcontact" method="POST" accept-charset="UTF-8">
…
</form>
</article>
The javascript code creating dynamically the form with the methods createElement, setAttribute and appendChild
is encapsulated in the function display_contact_form
(the entirety of the code is not given below for greater ease of reading) :
display_contact_form = function() {
/** Getting the tag article */
v_footer = document.getElementsByTagName('article')[0];
/** Form element creation */
v_form_contact=document.createElement('form');
v_form_contact.setAttribute('id','formcontact');
v_form_contact.setAttribute('method','post');
v_form_contact.setAttribute('accept-charset','UTF-8');
/** Subject field creation */
v_input_sujet = document.createElement('input');
v_input_sujet.setAttribute('name','subject');
v_input_sujet.setAttribute('required',true);
v_input_sujet.setAttribute('placeholder','Subject...');
/** Adding the field subject in the form */
v_form_contact.appendChild(v_input_sujet);
…
v_input_label = document.createElement('label');
v_input_label.appendChild(document.createTextNode('Remark'));
v_input_label.setAttribute('class','remark');
v_input_antispam = document.createElement('input');
v_input_antispam.setAttribute('name','remark');
v_input_antispam.setAttribute('pattern','[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$');
v_input_antispam.setAttribute('placeholder','name@domain.com');
v_input_antispam.setAttribute('class','remark');
v_form_contact.appendChild(v_input_label);
v_form_contact.appendChild(v_input_antispam);
…
/** Adding the form at the end of the tag article*/
v_footer.appendChild(v_form_contact);
};
The function display_contact_form
is called after the page loading with the method addEventListener
of the object window
.
display_contact_form = function() {
…
};
if(window.addEventListener) {
window.addEventListener("load",display_contact_form, false); // IE9, Chrome, FireFox
}
This mechanism is inserted in the header of the page (<head>...</head>
) or in a javascript script (lib.js
)
loaded in the page header.
<head>
…
<script type="text/javascript" src="../js/lib.js"></script>
…
</head>
Lighter alternative with Javascript
If the code for creating the form is too heavy or if the form creation is managed by a PHP script that can not be modified (software plug in…), a lighter alternative in javascript is still possible.
The anti spam hidden field and its label are added dynamically with Javascript within the form after the page loading.
The CSS class remark
(display:none
) is obviously applied to these 2 elements.
add_antispamfield = function() {
v_form_contact = document.getElementById('formcontact');
v_input_label = document.createElement('label');
v_input_label.appendChild(document.createTextNode('Remark'));
v_input_label.setAttribute('class','remark');
v_input_antispam = document.createElement('input');
v_input_antispam.setAttribute('name','remark');
v_input_antispam.setAttribute('pattern','[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$');
v_input_antispam.setAttribute('placeholder','name@domain.com');
v_input_antispam.setAttribute('class','remark');
v_form_contact.appendChild(v_input_label);
v_form_contact.appendChild(v_input_antispam);
};
if(window.addEventListener) {
window.addEventListener("load",add_antispamfield, false);
}
If the field remark
is not empty, it is a spam : data are discarded.
if ($_POST['remark'] != "") { die(); }
If the field remark
does not exist, so in this case the form has been managed by a spam robot from the HTTP response, data are also discarded :
if ( ! isset($_POST['remark']) ) { die(); }
An additional trick using IntersectionObserver
When possible, display forms as late as possible in the page load. When the form is at the bottom of the page,
one more trick to better protect the form from spam robots : the IntersectionObserver
API are very useful
to display the form only when the viewport intersects the form location.
It further decreases the likelihood that spam robots discover the form in the DOM.
In the code sample, the contact form is no more fully built and added through the load
event.
- The
form
element is created and added in the DOM, but theform
is empty, no submit elements. - The function
display_contact_form()
now only appends child elements in the form (input fields…). - Using
IntersectionObserver
, if theform
intersects the viewport, the functiondisplay_contact_form()
is fired.
/** Getting the tag article */
v_footer = document.getElementsByTagName('article')[0];
/** Form element creation */
v_form_contact=document.createElement('form');
v_form_contact.setAttribute('id','formcontact');
v_form_contact.setAttribute('method','post');
v_form_contact.setAttribute('accept-charset','UTF-8');
/** Adding the form at the end of the tag article*/
v_footer.appendChild(v_form_contact);
display_contact_form = function() {
/** Subject field creation */
v_input_sujet = document.createElement('input');
v_input_sujet.setAttribute('name','subject');
v_input_sujet.setAttribute('required',true);
v_input_sujet.setAttribute('placeholder','Subject...');
/** Adding the field subject in the form */
v_form_contact.appendChild(v_input_sujet);
…
v_input_label = document.createElement('label');
v_input_label.appendChild(document.createTextNode('Remark'));
v_input_label.setAttribute('class','remark');
v_input_antispam = document.createElement('input');
v_input_antispam.setAttribute('name','remark');
v_input_antispam.setAttribute('pattern','[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$');
v_input_antispam.setAttribute('placeholder','name@domain.com');
v_input_antispam.setAttribute('class','remark');
v_form_contact.appendChild(v_input_label);
v_form_contact.appendChild(v_input_antispam);
…
};
obsform = new IntersectionObserver(
(entries, observer) => { entries.forEach( entry => {
if (entry.isIntersecting===true) {
display_contact_form();
obsform.unobserve(entry.target);
}
});
}
, {root: null, rootMargin: '0px', threshold: 0.1} );
obsform.observe(v_form_contact);
Obviously, this trick has no sense if the form is at the top of the page : in this configuration, the observer would immediately the function
display_contact_form()
.
Conclusion
Against spam robots, hiding forms in the HTTP response or dynamically creating anti spam fields in the forms with Javascript, are efficient enough and very easy to implement. No need of Captcha technologies.
Zero spam here since this method has been implemented.