Open-source project
wargio/naxsi avatar
wargio/naxsi

NAXSI: a drop-by-default WAF module for nginx

NAXSI is an open-source, high performance, low rules maintenance WAF for NGINX

527 stars51 forksCGPL-3.0

At a glance

What is it?
NAXSI is a third party nginx module that behaves like a firewall with everything denied. It reads a small set of readable rules covering the characters and keywords that have no business appearing in a URI, and then the administrator's job is to add back the exceptions the site genuinely needs, either by hand from the error log or by letting the module learn them. The result is a web application firewall that does not need a signature update when a new attack appears.
Who is it for?
NAXSI suits a team that runs nginx in front of its applications and wants a firewall that does not need updating every time a new vulnerability is published, and that is prepared to spend a learning phase working out what the application legitimately does.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 32 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Everything denied, then add back what you need

NAXSI stands for Nginx Anti XSS and SQL Injection, and the README describes it as an open-source, high performance web application firewall for nginx with low rules maintenance.

Technically, it is a third party nginx module, available as a package for many UNIX-like platforms. By default it reads a small subset of simple and readable rules containing 99 percent of known patterns involved in website vulnerabilities. The examples given are the characters and keywords that are not supposed to be part of a URI, such as the less-than sign, the pipe character and the word drop.

That is the whole rule set, and it is worth understanding why it is so small. A URI is supposed to contain a path and a query string. A less-than sign in a query string is either an attempt to break out of markup or a mistake, and a pipe character is either a database attempt or a mistake. The rules do not need to know which, because the answer is the same either way: refuse it and ask the administrator to allow it if it turns out to be legitimate.

So Naxsi behaves like a drop-by-default firewall, and the only task is to add the required ACCEPT rules for the target website to work properly.

The consequence is that the work is at the start rather than forever. A signature-based firewall needs a new rule when a new attack appears. This one needs a new ACCEPT rule when a new legitimate request pattern appears, which is a bounded set that you discover once and then keep.

The licence is GPL-3.0, and the README makes the point in both senses: Naxsi is free software, as in freedom, and free, as in free beer.

Learning mode, and why there is no signature base

Because the patterns are simple, they will sometimes match legitimate queries, and the README is direct about who owns that problem: it is the Naxsi administrator's duty to add specific rules that will whitelist legitimate behaviours.

There are two ways to do that. The administrator can either add whitelists manually by analyzing nginx's error log, or, and this is the recommended path, start the project with an intensive auto-learning phase that will automatically generate whitelisting rules regarding the website's behaviour.

So the deployment sequence is: run in learning mode, let the module record what the application legitimately asks for, review what it learned, convert that into accept rules, and turn learning off. The learning phase is the interesting engineering step, because it is the point where the firewall learns the application.

The second paragraph of the README explains the difference from the alternatives, and it is the core argument for the project. Contrary to most web application firewalls, Naxsi does not rely on a signature base like an antivirus, and thus cannot be circumvented by an unknown attack pattern.

That is a real difference in kind, not degree. A signature firewall knows about the attacks its authors have written rules for, and an attack that does not match any of them passes through. Naxsi does not know about attacks at all; it knows that certain characters do not belong in a URI, and that principle does not go out of date when a new vulnerability is published.

The cost is the same in reverse. A signature firewall's rules are shared between sites and are maintained centrally. Naxsi's accept rules are specific to one application and have to be written by whoever runs it, which is the trade the whole design is built around.

Where it runs, and the fork that keeps it alive

The compatibility section is short. Naxsi should be compatible with any nginx version. It depends on libpcre for its regular expression support, and it is reported to work great on NetBSD, FreeBSD, OpenBSD, Debian, Ubuntu and CentOS.

The dependency on libpcre is worth noticing next to the container build, because the Dockerfile installs pcre2-dev, which is the newer branch of the same library family. The module is written in C, and the repository has a .clang-format configuration, so it is compiled rather than interpreted into nginx.

Then there is the paragraph about why this repository exists, which is unusual to find in a README and is worth reading in full. The original project, nbs-system/naxsi, has been officially archived as of November 8th, 2023. However, you can still request support, as the author remains the last active developer and is committed to maintaining it in this fork.

So the maintenance story is that a project which had a single upstream became a fork kept alive by the person who was the last active developer of it. That is a common pattern for infrastructure software and it has an obvious consequence for an adopter: there is one person whose commitment determines whether the project continues.

The documentation moved with it, and is at wargio.github.io/naxsi, with the repository's docs/ directory holding the sources. The last push to the repository was on 2026-09-21, and the recent releases are 1.5 in September 2023, 1.6 in October 2023 as a security update, and 1.7 in December 2024.

Building it, and the submodule step people forget

The build section starts with a warning that is worth more than the rest of the paragraph: be sure when you clone the repository to fetch all the submodules.

The instructions are the standard nginx module recipe, and they are short.

bash
$ git clone --recurse-submodules https://github.com/wargio/naxsi.git
$ wget --no-clobber -O nginx.tar.gz "https://nginx.org/download/nginx-1.22.0.tar.gz"
$ mkdir nginx-source
$ tar -C nginx-source -xzf nginx.tar.gz --strip-components=1
$ cd nginx-source
$ ./configure  --prefix=/etc/nginx --add-dynamic-module=../naxsi/naxsi_src
$ make

The important part is the configure line. The --add-dynamic-module option with the path to the naxsi_src directory is what makes nginx build the module alongside itself, and it is why the module can be installed as a package for many UNIX-like platforms rather than requiring a patched nginx.

The README pins nginx 1.22.0 in this example, while the container build in the Dockerfile uses a newer nginx, and the two are not in step. That is the kind of drift worth noticing in a fork, where the README predates the automation.

For the container route, the Dockerfile is a two stage build on Alpine. The build stage installs openssl-dev, pcre2-dev, zlib-dev, wget and build-base, downloads the nginx source, and then does something that is worth looking at closely: it patches the nginx source before compiling, replacing the value of NGX_MAX_ERROR_STR from 2048 with 8192 in src/core/ngx_log.h.

The container build, the hardening flags and the configs

The rest of the Dockerfile explains the shape of the shipped image and, incidentally, some good practice worth copying.

The compile flags are the hardened ones. The compiler options include -g, -O2, -Wextra and -Wall, then -fstack-protector-strong, -Wformat with -Werror=format-security, -fPIC, -Wdate-time and -D_FORTIFY_SOURCE=2. The linker options are -Wl,-z,relro and -Wl,-z,now, which enable full relro and immediate binding.

The nginx configure line selects the modules deliberately: the SSL module, the gzip static module, the realip module and the HTTP/2 module are included, while the mail modules and the uwsgi and scgi modules are left out. The log paths are set to /nginx/logs/access.log and /nginx/logs/error.log, and the prefix to /nginx.

The patch to NGX_MAX_ERROR_STR is the most interesting line, because it explains the design. Nginx limits the length of an error message string, and a web application firewall produces long error messages, because a rejected request needs to say what it rejected and why. Raising the limit from 2048 to 8192 is a small change with a clear purpose: without it, the module's own diagnostics get truncated, and a truncated diagnostic is the thing that makes learning mode hard to use.

The runtime stage copies the rule set from naxsi_rules/ into /nginx/naxsi/, along with the per-distribution configuration files from distros/nginx/, including naxsi_learning_mode.conf and naxsi_denied_url.conf. Those two files are the operational switches of the whole system, one that puts the module into learning mode and one that defines what a denied request looks like.

The repository layout matches: naxsi_src/ for the module source, naxsi_rules/ for the rules, distros/ for the packaged configurations, unit-tests/ for the test suite, docker/ for the container pieces, and a .scripts/ directory. Licensing is handled with REUSE.toml and a LICENSES/ directory, which is the standard way of recording licence information per file for a project that mixes licences.

Where NAXSI is the wrong choice

There are cases where a drop-by-default web application firewall is the wrong tool, and they come from the same place as the design.

The first is a site whose legitimate request surface is large and shifting. A web application with a search interface, a filter on every field, a download endpoint with a signed URL and an API consumed by other applications will generate a wide range of legitimate requests, and every one of them has to become an accept rule. That work is front-loaded but it is also permanent, because the application keeps changing.

The second is the learning phase itself. Learning mode has to be run with the application in front of traffic that resembles production, and the accept rules it produces have to be reviewed rather than trusted, because an attacker during that window teaches the firewall to allow something it should not. That is an operational commitment, not a configuration step, and it is the part of the deployment that teams underestimate.

The third is that the module works on requests. A request-side firewall that blocks dangerous characters cannot see everything a response might carry, and a rule set built around characters covers the injection families those characters belong to rather than the application logic. The README itself is careful to say what it does, listing characters and keywords, which is a smaller claim than a signature firewall's.

The alternative approach is a signature-based web application firewall, or a managed one at the edge of your network. The difference in approach is the maintenance model. A signature firewall needs its rule set updated as attacks appear and as applications change, and that work is done once for many sites by people whose job it is. NAXSI needs no attack updates and per-site accept rules that you write yourself, and that is a good trade for a small number of stable applications and a poor one for a large number of changing ones.

Editorial conclusion

NAXSI suits a team that runs nginx in front of its applications and wants a firewall that does not need updating every time a new vulnerability is published, and that is prepared to spend a learning phase working out what the application legitimately does. It does not suit a site with a large and constantly changing surface, such as one where every query parameter is dynamic and the legitimate patterns are hard to enumerate, and it does not suit an organisation that wants a managed rule set with a vendor behind it. Check three things before you deploy it: that you can run a learning phase with the application in front of real traffic and then turn learning off, because a WAF left in learning mode teaches the wrong lesson, that your nginx is built as a dynamic module with the add-dynamic-module option, since that is how the module is loaded, and that the plugin dependencies are present, since it needs libpcre for its regular expressions. The last push to the repository was on 2026-09-21 and the newest release is 1.7.

Frequently asked questions

What is NAXSI?

NAXSI, which stands for Nginx Anti XSS and SQL Injection, is a third party nginx module and web application firewall. It reads a small set of simple rules covering characters and keywords that should not appear in a URI and blocks them by default, so the administrator's job is to add back the legitimate behaviours as accept rules.

How do you configure NAXSI for a site?

Either by adding whitelists by hand after analyzing the nginx error log, or, as the README recommends, by starting with an intensive auto-learning phase that generates the whitelisting rules automatically. In short, Naxsi behaves like a drop-by-default firewall whose only task is to add the required accept rules for the site to work.

How is NAXSI different from a signature-based WAF?

The README argues that Naxsi does not rely on a signature base like an antivirus and therefore cannot be circumvented by an unknown attack pattern, because it works on the principle that certain characters do not belong in a URI. The trade is that the accept rules are specific to each application and have to be written by its administrator.

How do I build NAXSI from source?

Clone the repository with the recurse-submodules flag, fetch and unpack the nginx source, then configure nginx with the add-dynamic-module option pointing at the naxsi_src directory and run make. The README warns specifically that you must fetch all the submodules when you clone.

Why was NAXSI forked into the wargio repository?

The original project, nbs-system/naxsi, was officially archived as of November 8th, 2023. The fork's author states that he remains the last active developer and is committed to maintaining it here, so the project continues with one person responsible for it. The documentation moved to wargio.github.io/naxsi.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. README
  4. Releases
  5. wargio/naxsi on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wargio-naxsi.svg)](https://hysenlabs.com/projects/wargio-naxsi)

Was this useful?