Simple CAPTCHA

Image

To protect your forms from bots, you can require users to fill out a Simple Captcha text as seen on a dynamically created image.

Configuration

You might want to change how the Captcha looks. This can be done with the captcha.set config option.

/site/config/config.php
<?php

return [
    'bnomei.klub.captcha.set' => function () {
        // https://github.com/S1SYPHOS/php-simple-captcha
        $builder = new \SimpleCaptcha\Builder;
        $builder->bgColor = '#FFFFFF';
        $builder->lineColor = '#FFFFFF';
        $builder->textColor = '#000000';
        $builder->applyEffects = false;
        $builder->build();
        kirby()->session()->set('captcha', $builder->phrase);

        return [
            'captcha' => $builder->inline(),
        ];
    },
    // other options...
];

Securing Forms

The plugin does not provide a snippet to render the image. However, the following example shows how to use Alpine.js to render the image dynamically, allowing for manual refreshes.

<form action="<?= site()->url() ?>/klub/login" method="POST">
    <div x-data="{
    captcha: undefined,
    refresh() {
      fetch('<?= site()->url() ?>/klub/captcha')
        .then(response => response.json())
        .then(data => {
          this.captcha = data.captcha;
        })
    }
  }" x-init="refresh()">
        <label>
            <span>Captcha: </span>
            <input name="captcha" type="text" value=""
                   required pattern="[a-zA-Z0-9]{5}">
        </label>
        <figure>
            <img :src="captcha" width="150" height="40"/>
        </figure>
        <button x-on:click="refresh()" type="button">Refresh</button>
    </div>
    <input type="email" name="email" placeholder="Email" required
           value="<?= get('email') ?>">
    <input type="hidden" name="redirect" value="<?= page('account')->url() ?>">
    <input type="hidden" name="token" value="<?= csrf() ?>">
    <button type="submit">Login</button>
</form>

Behind the Scenes

The endpoints intended for public use, which are most likely targeted by bots (login, register/signup, magic-link), are preconfigured with a check for the Simple Captcha.

If posted with the form, the klub()->captcha()-helper will query the current request for the captcha and check if the form is legit or not.

Even if you use Captcha, you should keep the csrf -check as an additional layer of security.

On success the form will continue as intended. If not, it will yield a 401 HTTP status code.

Kirby Klub is not affiliated with the developers of Kirby CMS. We are merely standing on the shoulder of giants.
© 2025 Bruno Meilick All rights reserved.