Install

Works with Drupal: ^10.6 || ^11

Using Composer to manage Drupal site dependencies

Alternative installation files

Download tar.gz 709.89 KB
MD5: 7ddf08950e8499b6303fbb1e17baff34
SHA-1: da706e3297fddcd874f39d926dcbf47ae7a8cdc6
SHA-256: ee7b5255ef372f9e43eb85816b2ec4a83c3d72346cde19acec46258329a23213
Download zip 921.58 KB
MD5: 7a63c4a3ae9a0d3bd3603f5078b8f8a2
SHA-1: 9bbf6941e16fa816060ed19cd6dcb6d973ec7b0f
SHA-256: 94c58a68979d9f55a662c4b5480a1e38e2636ad87a84957efea227792dffc126

Release notes

Brings the module up to kanopi/firewall 2.37.0. The headline changes are what a blocked or challenged visitor sees, a log page that stays fast on a large table, and two bug fixes for sites in exception mode.

## Before you upgrade

- **Requires `kanopi/firewall` ^2.37.** Run `composer update kanopi/firewall` with this release. The new floor brings two library fixes this module exposes:
- **Pantheon storage connected to the wrong database.** The `storage-pantheon.yml` preset connected to `db:3306` instead of Pantheon's database, so the firewall let every request through.
- **One host's config could be served to another.** A config whose `%env()` read a request-scoped value could be cached and handed to a different host.
- **Run database updates.** A post-update hook adds two new indexes to an existing firewall log table (below). On a log table with millions of rows, adding them takes a while. On InnoDB, writes carry on while the indexes build.

## In exception mode, visitors now see what you configured (#98, #102)

Two things were wrong on sites that run the firewall in **exception** mode, where the module writes the response itself:

- **A blocked visitor saw a blank 403.** The banning message on the General tab never reached anyone in this mode. It does now, with the same headers the library sends in block mode.
- **A solved challenge reported failure.** The challenge page expects a JSON reply and got a redirect. With ALTCHA, that meant "That verification could not be used. Fetching a new one…" and a reload that then let the visitor in. With math and Turnstile, it meant "Verification failed" even though the visitor had passed. The module now answers the way the library does.

## What a blocked visitor sees (#99)

On the General tab, a block can now be answered with an **HTML page** instead of one line of text: a language, a title and a heading, your own CSS or stylesheet, or a whole template of your own. It's sent with a strict Content-Security-Policy. You can also choose to **answer API clients with JSON**. A **lockdown** gets its own message and the same page option.

A **block rule** can say why it refused, with its own message ("Automated access to search is not allowed") and its own page heading.

Every color on these pages, and on the challenge page, is a CSS custom property, so `:root { --fw-accent: #0b5; }` themes all of them.

## The challenge page's wording (#100)

The Challenge tab has a **Page** section for the interstitial: language, browser tab title, heading, introduction, button, the message after a wrong answer, and styles.

Values the library would refuse to start with are refused by the forms instead of saved. That covers CSS that closes its style tag, a stylesheet that isn't a path or `https:`, and a malformed language. A saved value the library refused would leave every rule unenforced.

## A log page that stays fast (#96, #97)

On a site with 2.2 million log records, the Log tab took seconds to load. It now:

- opens on the **last day**, and "All history" says it reads the whole table;
- counts up to **10,000** and shows `10,000+` past that;
- pages **Older/Newer** from the last row shown, so a deep page costs the same as the first;
- refuses **text search over all history**, because "contains" can't use an index.

The library's two new indexes on the log table make per-rule totals for a day about 20 times faster (0.42 s down to 0.02 s on 2.8M rows). They reach an existing table **without anyone running anything**:

- **Updates:** the post-update hook adds them when database updates run.
- **Cron:** adds them whenever the installed library changes. Automated cron is enough.

`drush basic-firewall:migrate-schema` (with `--dry-run`) is there to do it now or see the SQL first. The status report lists anything still missing.

## The parsed configuration can live in Drupal's cache (#101)

With **Drupal's cache** chosen as the Storage tab's cache backend, the library's parsed copy of the compiled configuration now goes to that cache (Redis, on a site with Redis) instead of a file in the private directory. On hosting where that directory is a network mount, a request then reads and writes no cache file for it. A rebuild is still seen immediately.

## Compatibility

- **New floor:** `kanopi/firewall` ^2.37.
- **Behavior changes:**
- Sites in **exception** mode now show blocked visitors the banning message, where they used to get an empty page.
- The **Log** tab now opens on the last day, not all history.
- **Nothing to re-save.** The new settings default to what happened before, so a site that changes nothing compiles the same firewall configuration.

Created by: sean_e_dietrich
Created on: 3 Oct 2026 at 18:30 UTC
Last updated: 3 Oct 2026 at 18:31 UTC
Bug fixes
New features

Other releases