First of all, the trick that I'm going to reveal could work on any website and not only WordPress. But keep in mind you will need some website development knowledge to understand the article. I'll try to explain the process in detail, but at some point, I had to take decisions for this article not to be 22 pages long! So for instance, I'm considering you know how to load a custom javascript file, install a new library if needed, etc. If you have a question, feel free to write it in the comment section.
The facts
It's not a breaking news to tell you that spam creators and haters are in war basically since the birth of the Internet. This is a great deal and a lot (really, a lot) of money is both generated and wasted on this.
For spammers (not speaking about email), it's just about finding forms on forums and news websites which they can automate (using bots, etc.) advertisement or fishing.
WordPress and it's comments feature is of course the target of spammers. So by default, WordPress is provided with an incredibly efficient plugin for spam: Akismet. Once set up, Akismet will detect if a new posted comment is spam or not and depending on your settings, silently discard the worst and most pervasive spam or put it in the Spam folder for review (auto deleted after 15 days). Akismet is so smart (thanks to it's huge database) to detect those spams that all people live with it.
But for me, something is still wrong: this doesn't prevent bots to post. For each bot actions, there are a huge number of requests made on your website. Those for accessing your website, those for posting a comment, those for Akismet to deal with it, etc. What about preventing bots to physically access your comment form and post (or at least try)? This would save quite some processing time.
How to drastically reduce WordPress comment spam
Extract of my Akismet stats over two years
Starting June 2015, I've updated my website to try out a simple trick (using the .htaccess
file) found on internet to prevent bots to directly load the comment form wp-comments-post.php
. As you can see, from june to august 2015, it had some effects.
But in September 2015, I've added to this a personnal modification made on my own theme. Was just a try but results above are, I guess, self explanatory.
In fact, the trick is quite simple. If you have not done it yet (else refresh your browser), scroll down this page until you reach the end of this article, you'll notice that the comments and the comment form are not loaded. As soon as the scroll view start revealing the area where the comments should be, an Ajax request is sent to load the comments and the form. Scrolling is something bots don't do, so obviously, when visiting this page, there is no form to post on. Boom, simple.
Of course, the scroll based triggering is a choice by design. It obviously means that if your post content is to small or someone's browser window too high, the comments section will instantly reveal, thus allowing spam to be posted. So this scroll based solution cannot prevent “all” spam to be catched, but that's fine for me as you have to consider and put on the balance the usability for your readers. An instantly “more restritive” way would be to trigger the loading by asking the user to click a button. All of this is up to you to decide.
Let's do some killing!
Step 1: .htaccess. Prevent direct access to comment form.
Here's the code you must add to the .htaccess
file located in your WordPress website root.
Change the tazintosh.com
to your own website domain (line 9).
# ----------------------------------------------- # Anti Spam (prevent direct usage of wp-comments) # ----------------------------------------------- <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_METHOD} POST RewriteCond %{REQUEST_URI} .wp-comments-post.php* RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !.tazintosh.com.* [OR] RewriteCond %{HTTP_USER_AGENT} ^$ RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L] </IfModule>
Step 2: Ajax trick.
You'll have to edit some of your theme templates. If like me, you've created your own theme from scratch, it will be even easier. Basically, the point is to prevent the comment section to be generated on PHPs files. Most of the time on a theme, comments are loaded into single.php
and page.php
files (see the Wordpress Codex to learn more.
Preventing default loading of comments
Search for something similar to the following on each of your related template files:
<?php if ( comments_open() || get_comments_number() ) : comments_template(); endif; ?>
Comment those lines or delete them. Remember that if you're modifying a theme which is not yours, each update will remove your changes. I leave up to you the care of dealing with this: Either make some backups, either start using Subversion, etc.
<?php // if ( comments_open() || get_comments_number() ) : // comments_template(); // endif; ?>
Once these modifications are done, if you refresh your website, comments should'nt load anymore. Now, we need to set modify your theme comments.php
and create a new function that the Ajax will be able to call.
Modifying comments.php
If you look for this file on default themes like twentyfifteen, twentyfourteen, twentysixteen, it basically contains the <div id="comments">[…]</div>
. Well, we'll surround this by a new function and replace lines 14 to 16 with your template comments code.
<?php function taz_loadComments(){ global $comments_post_id; $comments_post_id = $_GET['theid']; global $comments_post_id; $args = array( 'post_id' => $comments_post_id ); $comments_query = new WP_Comment_Query; $comments = $comments_query->query($args); ?> <div id="comments"> <!-- […] --> </div> <?php die(); } add_action('wp_ajax_nopriv_loadComments', 'taz_loadComments'); // If user not logged in add_action('wp_ajax_loadComments', 'taz_loadComments'); // For logged in user ?>
Now, we need to set up a javascript function that will load this template in Ajax, only when needed. If you're using a theme that support custom javascript, this is probably where you'll have to put this code.
Loading comments section with Ajax
$.ajax({ url: '/wordpress/wp-admin/admin-ajax.php', type: 'GET', dataType : 'html', data: { action: 'loadComments', theid: BODY.data('id'); // See note* } }).done(function(data){ if (data !== 0){ // The code to append the comments, i.e. $('.parentToAppendTo').append(data), and anything else you need. } else { // What to do in case of error. } });
header.php
file to automatically generate the corresponding ID:
<?php if (in_array('blog',$classes)) { $documentId = get_option('page_for_posts'); } else { $documentId = get_the_ID(); } ?> <body data-id="<?php echo $documentId ?>"> <!-- […] -->
$(document).ready(function(){ // The following code must run on DOM Ready if (!BODY.hasClass('blog') && !BODY.hasClass('archive') && !BODY.hasClass('search')){ var commentsController = new ScrollMagic.Controller(); // Init controller // Build scene new ScrollMagic.Scene({triggerElement: ".taz_mainSection-footer", triggerHook: 'onEnter'}) .addTo(commentsController) .reverse(false) .on("start", function () { $.ajax({ url: '/wordpress/wp-admin/admin-ajax.php', type: 'GET', dataType : 'html', data: { action: 'loadComments', theid: BODY.data('id'); } }).done(function(data){ if (data !== 0){ $('.taz_mainSection-footer').append(data); // Other stuffs going on here. } else { console.log("Error"); } }); }); } });
That's it… I guess. We've added a rule to the .htaccess
file to stop bots from directly accessing the comment form, we've modified some theme templates to prevent comment section being generated by default and we managed to do this via an Ajax request, only when needed. From now on, I see no reason for you not to have statistics similar to mines. I know this will be some work to do, but boy, it worth any minute spent. Enjoy your spam free website!