NGINX / Security

How to install and configure CAPTCHA NGINX module in CentOS/RHEL

by , , revisited on

We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

Step 1. Add GetPageSpeed extras YUM repository

First, run the following command to add our repository:

sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm

Step 2. Install NGINX and CAPTCHA module

Install latest stable NGINX:

sudo yum -y install nginx

Install the required modules:

sudo yum -y install nginx-module-form-input nginx-module-set-misc nginx-module-captcha

Enable installed modules

Add the following at the top of /etc/nginx/nginx.conf:

load_module modules/ndk_http_module.so;
load_module modules/ngx_http_set_misc_module.so;
load_module modules/ngx_http_form_input_module.so;
load_module modules/ngx_http_captcha_module.so;

Set up sample captcha configuration

location = /captcha {
location = /login {
    set_form_input $csrf_form _token;
    set_unescape_uri $csrf_unescape $csrf_form;
    set_form_input $captcha_form captcha;
    set_unescape_uri $captcha_unescape $captcha_form;
    set_md5 $captcha_md5 "secret${captcha_unescape}${csrf_unescape}";
    if ($captcha_md5 != $cookie_captcha) {
        # captcha invalid code


Visit example.com/captcha?csrf=test and observe the captcha image being loaded in the browser.


Refer to the documentation for additional configuration details.

Implementation on a website

You must implement the captcha on your website via HTML code. You can insert simple HTML of the image tag:

<img src="/captcha?csrf=..." />

The csrf argument value

The csrf argument is required to prevent cross-site scripting attacks. Your application’s framework must generate a CSRF token.

For example, in Laravel 8, you can get the CSRF token via the csrf_token() helper function.

Taking Laravel 8, for example, we have the following Blade template:

<form method="POST" action="/profile">

    <!-- Equivalent to... -->
    <input type="hidden" name="_token" value="{{ csrf_token() }}" />

In our NGINX configuration above, the set_form_input $csrf_form _token; takes the value of the submitted _token argument, which contains the CSRF token, and uses it in combination with the validation of captcha input.

How it works

When an image is loaded via /captcha?csrf=... request, it sends an HTTP cookie to the browser, e.g.:

Captcha=b4727e2bc1905f55d5ef07b83639aa76; Max-Age=300

In our NGINX configuration which is implemented for the login page, we have a check whether captcha was valid:

    if ($captcha_md5 != $cookie_captcha) {
        # captcha invalid code

In there we can implement various things, e.g. incrementing login count (with some Lua scripting), or simply returning an error page if you don’t tolerate failed captchas at all, e.g.

    if ($captcha_md5 != $cookie_captcha) {
        return 403;

This allows reducing the load from bots in a more efficient way because, in the case of valid input, the corresponding framework is not being loaded.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.