Generate CAPTCHA Image in PHP (GD Library)
CAPTCHA images are used to prevent automated form submission. This post demonstrates how to add CAPTCHA image in PHP as an extra layer to your HTML forms and prevent automated form submission.

CAPTCHA images are used in online forms to confirm the form is submitted by a real human and not some robot. CAPTCHA images contain some random characters with some distortion, blur and some random lines which is not readable by a robot. Thus ensuring the form is submitted by a human.
In this post we will create a PHP CAPTCHA image class with some image manipulation functions. We will then use this class to generate CAPTCHA image in PHP. The main functions to mention in this class are add_noise(), add_blur() and get_random_string().
Files we are going to create for this post are:
- captcha.php: The PHP class to create captcha image.
- captcha-image.php: The PHP script to create instance of captcha class and generate a captcha image.
- index.php: The main index page which will contain a form with CAPTCHA field.
- style.css: The CSS styles for HTML page and CAPTCHA form.
How to Generate and Validate CAPTCHA Image in PHP
Generating CAPTCHA image in PHP can be achieved with few simple steps. The step to generate CAPTCHA image in PHP are:
- Create a PHP image CAPTCHA class for image manipulation. This file will generate CAPTCHA image with random string.
- Create another PHP file to receive submitted form for processing and compare the CAPTCHA string passed in form against the string generated by CAPTCHA class.
- Create a user interface with example HTML form for submission along with CAPTCHA string.
- Add CSS styles for user interface and test the CAPTCHA image implementation.
Step 1: Create Image Captcha Class in PHP (GD Library)
Our captcha class will be using GD Library to generate CAPTCHA image. This class will house methods for generating GD image resource, Adding noise to image, Adding blur to image and return image to browser. The constructor method of this class will generate CAPTCHA image with the provided background color for image. The class definition is as below:
$image:Holds the image resource created by the GD library.$color:Holds the color used for the captcha text.$width:Holds the width of the captcha image.$height:Holds the height of the captcha image
And following methods will be used:
add_noise(): Adds noise to the image by setting random pixels to a specified color.add_blur(): Applies a Gaussian blur filter to the image multiple times.get_image(): Generates the captcha text, renders the image as a JPEG binary string, and destroys the image resource.get_decimal_color: Converts a hexadecimal color string to its RGB equivalent and returns an array of colors containing "red", "green" and "blue" colors.get_true_color(): Allocates a color for the image using RGB values and returns integer color.get_random_string(): Generates and returns a random string of the specified length using alphanumeric characters.
captcha.php
<?php
class captcha
{
/**
*
* @var GdImage|false
*/
private $image;
/**
*
* @var int
*/
private $color;
/**
*
* @var int
*/
public $width;
/**
*
* @var int
*/
public $height;
/**
*
* @param int $width
* @param int $height
* @param string $background
* @param string $color
*/
function __construct(int $width = 120, int $height = 60, string $background = '#4F5B93', string $color = '#ffffff')
{
// Create captcha image, Set height & width, text color when object is initialized
$this->image = imagecreatetruecolor($width, $height);
$this->width = intval($width);
$this->height = intval($height);
$color = $this->get_decimal_color($color); // Get decimal color value from hex color value
$this->color = $this->get_true_color($color); // Set true color from decimal color
$background = $this->get_decimal_color($background);
$background = $this->get_true_color($background);
// Fill background color to image
imagefill($this->image, 0, 0, $background);
}
/**
* Function to add noise to image
* @param string $color
* @return void
*/
public function add_noise(string $color = '#8892BF'): void
{
$font_color = $this->get_decimal_color($color);
$font_color = $this->get_true_color($font_color);
// Loop x co-ordinates of image
for ($r = 0; $r < $this->width; $r++) {
// Loop y co-ordinates of image
for ($c = 0; $c < $this->height; $c++) {
// Add noise on step of 1 pixel, skipping one pixel and noise to next pixel
if (mt_rand(0, 1) == 1)
imagesetpixel($this->image, $r, $c, $font_color);
}
}
}
/**
* Adds blur filter to image
* @param int $count
* @return void
*/
public function add_blur(int $count = 3): void
{
$count = intval($count) > 10 ? 10 : $count;
for ($i = 1; $i <= $count; $i++) {
imagefilter($this->image, IMG_FILTER_GAUSSIAN_BLUR);
}
}
/**
* Returns the image as binary string
* @return string
*/
public function get_image(): string
{
$this->add_captcha_text();
$image = imagejpeg($this->image, NULL, 100);
imagedestroy($this->image);
return $image;
}
/**
* Returns decimal equivalent of hex color string
* @param string $color
* @return array
*/
private function get_decimal_color(string $color): array
{
$color = str_starts_with($color, '#') !== false ? substr($color, 1) : $color;
if (strlen($color) === 6) {
$red = hexdec(substr($color, 0, 2));
$green = hexdec(substr($color, 2, 2));
$blue = hexdec(substr($color, 4, 2));
} else {
$red = hexdec(str_repeate(substr($color, 0, 1), 2));
$green = hexdec(str_repeate(substr($color, 1, 1), 2));
$blue = hexdec(str_repeate(substr($color, 2, 1), 2));
}
return ['red' => $red, 'green' => $green, 'blue' => $blue];
}
/**
*
* @param array $color
* @return int
*/
private function get_true_color(array $color): int
{
return imagecolorallocate($this->image, $color['red'], $color['green'], $color['blue']);
}
/**
* Generates and returns random string
* @param int $length
* @return string
*/
private function get_random_string(int $length): string
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$random_string = '';
for ($i = 0; $i < $length; $i++) {
$random_string .= $characters[mt_rand(0, strlen($characters) - 1)];
}
return $random_string;
}
/**
* Adds random string as text to captcha image
* @return void
*/
private function add_captcha_text(): void
{
$font_file = realpath(dirname(__FILE__) . '/fonts/sketch_block.ttf');
$font_size = $this->height * 0.6;
$font_color = $this->color;
$random_string = $this->get_random_string(5);
$ttf_box = imagettfbbox($font_size, 0, $font_file, $random_string);
$xr = abs(max($ttf_box[2], $ttf_box[4]));
$xy = abs(max($ttf_box[5], $ttf_box[7]));
$x = intval(($this->width - $xr) / 2);
$y = intval(($this->height + $xy) / 2);
imagettftext($this->image, $font_size, 0, $x, $y, $font_color, $font_file, $random_string);
$_SESSION['captcha'] = $random_string;
}
}
Step 2: Create Captcha Image in PHP using CAPTCHA Class
Our captcha-image.php file will create CAPTCHA image and return image as response of page.
- Create an instance of captcha class providing the width and height for CAPTCHA image.
- Add noise to CAPTCHA image.
- Add blur to CAPTCHA image.
- Set
Content-Typeof page toimage/jpeg. - Return image as response.
captcha-image.php
<?php
if (!session_id()) {
session_start();
}
include_once 'captcha.php';
$captcha = new captcha(280, 60);
$captcha->add_noise(); // Add noise to captcha image
$captcha->add_blur(); // Blur captcha image
header('Content-Type: image/jpeg');
$captcha_image = $captcha->get_image();
die($captcha_image);
Step 3: Create HTML Form with CAPTCHA Field
Create an HTML page with form containing the CAPTCHA image and input field to enter captcha. Initially load the image returned from captcha-image.php file. Then the refresh the image with JavaScript click event. We also validate the CAPTCHA in this file.
index.php
<?php
if (!session_id()) {
session_start();
}
if (!empty($_POST)) {
$_SESSION['captcha_status'] = intval($_POST['captcha'] === $_SESSION['captcha']);
header(sprintf('Location: %s', $_SERVER['REQUEST_URI']));
exit();
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Generate Captcha Image in PHP - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"/>
<link rel="stylesheet" href="css/style.css"/>
<script type="text/javascript">
function refresh_captcha() {
let random = Math.random();
document.querySelector(".captcha-image").src = "captcha-image.php?" + random;
}
</script>
</head>
<body>
<section class="section py-4">
<div class="container">
<div class="captcha-box">
<?php
if (isset($_SESSION['captcha_status'])) {
if ($_SESSION['captcha_status']) {
?>
<div class="captcha-message">Captcha verified</div>
<?php
} else {
?>
<div class="captcha-message error">Captcha verification failed</div>
<?php
}
unset($_SESSION['captcha_status']);
} ?>
<form name="captcha_form" method="POST">
<img class="captcha-image" src="captcha-image.php"/>
<div><input type="text" name="captcha" class="form-control"/></div>
<p>Can't read? Click <a href="javascript:refresh_captcha();">here</a> to refresh</p>
<button type="submit" class="btn btn-green btn-block">Verify</button>
</form>
</div>
</div>
</section>
</body>
</html>
Step 4: Add CSS styles
Add CSS styles for entire HTML page and CAPTCHA form.
style.css
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
line-height: 1.5;
}
.container {
width: 100%;
max-width: 1140px;
margin-right: auto;
margin-left: auto;
padding-right: 15px;
padding-left: 15px;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.form-control {
padding: 10px;
border: 1px solid #ddd;
width: 100%;
margin-bottom: 5px;
color: #444;
}
.btn {
display: inline-block;
padding: 5px 10px;
cursor: pointer;
font: inherit;
}
.btn-block {
width: 100%;
}
.btn-green {
background-color: #00a65a;
border: 1px solid #00a65a;
color: #ffffff;
}
.captcha-box {
max-width: 300px;
background: #dddddd;
padding: 0.5rem;
}
.captcha-message {
background: #ffffff;
padding: 0.25rem;
text-align: center;
color: #009549;
border: 1px solid #009549;
margin-bottom: 1rem;
}
.captcha-message.error {
color: #dd1a16;
border: 1px solid #dd1a16;
}