Welcome to a tutorial on how to create a simple captcha in PHP. Have a website that is constantly being spammed by angry keyboard warriors? Time to put in some security and prevention measures. Let us walk through an example in this guide, without the use of any 3rd party frameworks – Read on!
TABLE OF CONTENTS
PHP CAPTCHA
All right, let us now get into the details of creating a simple PHP captcha.
STEP 1) PHP CAPTCHA CLASS
<?php
class Captcha {
// (A) CAPTCHA SETTINGS
private $length = 8; // number of characters
private $capW = 300; // captcha width
private $capH = 100; // captcha height
private $capF = "C:/Windows/Fonts/arial.ttf"; // font - CHANGE TO YOUR OWN!
private $capFS = 24; // captcha font size
private $capB = __DIR__ . DIRECTORY_SEPARATOR . "CapBack.jpg"; // captcha background
// (B) AUTO START SESSION
function __construct () {
if (session_status()==PHP_SESSION_DISABLED) { exit("Sessions disabled"); }
if (session_status()==PHP_SESSION_NONE) { session_start(); }
}
// (C) PRIME THE CAPTCHA - GENERATE RANDOM STRING IN SESSION
function prime () : void {
$char = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$max = strlen($char) - 1;
$_SESSION["captcha"] = "";
for ($i=0; $i<=$this->length; $i++) { $_SESSION["captcha"] .= substr($char, rand(0, $max), 1); }
}
// (D) DRAW THE CAPTCHA IMAGE
function draw ($mode=0) : void {
// (D1) FUNKY BACKGROUND IMAGE
if (!isset($_SESSION["captcha"])) { exit("Captcha not primed"); }
$captcha = imagecreatetruecolor($this->capW, $this->capH);
list($bx, $by) = getimagesize($this->capB);
$bx = ($bx-$this->capW) < 0 ? 0 : rand(0, ($bx-$this->capW));
$by = ($by-$this->capH) < 0 ? 0 : rand(0, ($by-$this->capH));
imagecopy($captcha, imagecreatefromjpeg($this->capB), 0, 0, $bx, $by, $this->capW, $this->capH);
// (D2) RANDOM TEXTBOX POSITION
$ta = rand(-20, 20); // random angle
$tc = imagecolorallocate($captcha, rand(120, 255), rand(120, 255), rand(120, 255)); // random text color
$ts = imagettfbbox($this->capFS, $ta, $this->capF, $_SESSION["captcha"]);
if ($ta>=0) {
$tx = rand(abs($ts[6]), $this->capW - (abs($ts[2]) + abs($ts[6])));
$ty = rand(abs($ts[5]), $this->capH);
} else {
$tx = rand(0, $this->capW - (abs($ts[0]) + abs($ts[4])));
$ty = rand(abs($ts[7]), abs($ts[7]) + $this->capH - (abs($ts[3]) + abs($ts[7])));
}
imagettftext($captcha, $this->capFS, $ta, $tx, $ty, $tc, $this->capF, $_SESSION["captcha"]);
// (D3) OUTPUT CAPTCHA
if ($mode==0) {
ob_start();
imagepng($captcha);
$ob = base64_encode(ob_get_clean());
echo "<img src='data:image/png;base64,$ob'>";
} else {
header("Content-type: image/png");
imagepng($captcha); imagedestroy($captcha);
}
}
// (E) VERIFY CAPTCHA
function verify ($check) {
if (!isset($_SESSION["captcha"])) { exit("Captcha not primed"); }
if ($check == $_SESSION["captcha"]) {
unset($_SESSION["captcha"]);
return true;
} else { return false; }
}
}
// (F) CAPTCHA OBJECT
$PHPCAP = new Captcha();
First, we start with the “core engine”, the Captcha library. Yes, this looks complicated at first, but keep calm and explore it slowly.
- (A) A whole bunch of captcha settings. Feel free to change to your own font, change the background, captcha image dimensions, etc…
- (B & F) When
$PHPCAP = new Captcha()
is created, the constructor automatically starts the session if it is not already started. - (C To E) There are only 3 “real captcha functions”.
prime()
Step 1 of the process, pretty much just generates a random string into$_SESSION["captcha"]
.draw()
Step 2 of the process, use this to output the HTML captcha image.verify()
When the user submits the form, use this to verify the user’s captcha against the session.
STEP 2) GENERATE CAPTCHA IN HTML FORM
<form method="post" action="3-submit.php">
<!-- (A) FORM FIELDS -->
<label>Name</label>
<input name="name" type="text" required value="Jon Doe">
<label>Email</label>
<input name="email" type="email" required value="jon@doe.com">
<!-- (B) CAPTCHA HERE -->
<label>Are you human?</label>
<?php
require "1-captcha.php";
$PHPCAP->prime();
$PHPCAP->draw();
?>
<input name="captcha" type="text" required>
<!-- (C) GO! -->
<input type="submit" value="Go!">
</form>
- The “usual form fields”.
- Generate the captcha.
- Captain Obvious to the rescue,
require "1-captcha.php"
to load the library. - Call the
$PHPCAP->prime()
function to generate a random captcha string into the session. - Call the
$PHPCAP->draw()
function to generate the captcha image.
- Captain Obvious to the rescue,
STEP 3) CAPTCHA VERIFICATION UPON SUBMISSION
<?php
// (A) LOAD CAPTCHA LIBRARY
require "1-captcha.php";
// (B) VERIFIED - DO SOMETHING
if ($PHPCAP->verify($_POST["captcha"])) {
$result = "Congrats, CAPTCHA is correct.";
print_r($_POST);
}
// (C) NOPE
else { echo "CAPTCHA does not match!"; }
On submitting the form, we only need to use $PHPCAP->verify($_POST["captcha"])
to check if the user has entered the correct captcha – Proceed if the challenge passed, or show an error message if it failed.
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
SUPPORT
600+ free tutorials & projects on Code Boxx and still growing. I insist on not turning Code Boxx into a "paid scripts and courses" business, so every little bit of support helps.
Buy Me A Meal Code Boxx eBooks
EXAMPLE CODE DOWNLOAD
Click here for the source code on GitHub gist, just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
EXTRA BITS & LINKS
That’s all for the code, and here are a few small extras that may be useful to you.
MORE SECURITY
Before the “security experts” start to bang on their keyboards, some additional security can be added to this simple captcha.
- Impose a time limit/expiry on the captcha, 10 minutes for example –
$_SESSION["cexpire"] = strtotime("now") + 600
. - Change the
verify()
function to also do a time check –if ($_SESSION["cexpire"] > strototime("now")) { NOPE }
. - Add a number of times a user can challenge the captcha. Decide what to do if the challenge fails too many times – Maybe require the user to click on an email verification link.
- Very simply, use HTTPS.
RELOADING THE CAPTCHA
Want to add a “reload captcha” button to the form? Some simple modifications will do the trick:
- “Move” the captcha creation to a separate
mycap.php
script –<?php require "1-captcha.php"; $PHPCAP->prime(); $PHPCAP->draw();
- Modify
2-form.php
, replace the captcha with an empty<div id="cap">
- Use Javascript fetch to load/reload the captcha.
var cap = () => fetch("mycap.php").then(res=>res.text()).then(txt=>document.getElementById("cap").innerHTML = txt);
window.addEventListener("load", cap);
<input type="button" value="Reload Captcha" onclick="cap()">
LINKS & REFERENCES
- PHP Sessions (Very Simple Examples) – Code Boxx
- GD Image Library – PHP
- Changed your mind? Want something better? Check out the free Google reCaptcha.
- Simple PHP Contact Form With Google reCaptcha – Code Boxx
THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you to better fight spam in your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
Hi!
Really a great job!
Just one question: a trick to reload the captcha only (not the whole page), in case it is difficult to read, in php?
TUtorial updated – See “RELOADING THE CAPTCHA” above.
Excellent code !!!…….
Followed your quick tips and executed successfully without any errors …
Keep uploading cool programs …..
Hi,
excellent code.
But I have a problem. The downloaded sources placed on my localhost (PHP 7.1.8) returns no captcha code (only the background is visible).
What’s wrong?
If it is only generating the image:
1) The captcha code is probably not primed into the session, I.E. $_SESSION[‘captcha’] is missing. Make sure that the session is started.
2) Alternatively, the font may not be set properly; GD cannot access the font file, cannot generate the image captcha properly.
Good luck!