# openresty/lua-nginx-module: Running Lua Inside NGINX Request Phases

> The ngx_http_lua_module embeds LuaJIT into NGINX, letting you run Lua code at rewrite, access and content phases. It is production ready per the README, but it is not shipped with the NGINX source and must be built or installed through OpenResty.

**openresty/lua-nginx-module** — Embed the Power of Lua into NGINX HTTP servers

- Repository: https://github.com/openresty/lua-nginx-module
- Website: https://openresty.org/
- Stars: 11,785 · Forks: 2,050
- Language: C
- License: not declared
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/openresty-lua-nginx-module

## What lua-nginx-module actually solves

NGINX configuration is declarative. You can match a URI, rewrite it, proxy it, and that is roughly the end of the vocabulary. The moment you need to inspect a request body, sign a URL with a per-request secret, or call an internal service to decide whether a request is allowed, plain configuration runs out.

ngx_http_lua_module answers that by embedding a Lua runtime into the NGINX HTTP request pipeline. The README describes it as a core component of OpenResty, and adds the sentence that matters most for adoption: "This module is not distributed with the Nginx source." It is a third-party module that hooks into NGINX, not a feature you get by downloading nginx.

The audience is engineers who already run NGINX at the edge and want logic there rather than in an upstream application. If your rule is "every request must pass through application code that can read headers, query strings and bodies, and can make outbound calls before deciding," this module is the mechanism that lets you do it without adding a second hop.

## The phase hooks: how Lua code gets control of a request

The module's design is built around NGINX request phases. The synopsis in the README shows directives named after those phases: rewrite_by_lua_block, access_by_lua_block and content_by_lua_block, plus the _file variants that load Lua from disk. A single location can use several of them, as in the /mixed example where rewrite.lua, access.lua and content.lua all run for the same request.

Each directive runs Lua at a defined point. Code in rewrite_by_lua runs before the location is chosen and can change the URI. Code in access_by_lua runs after that, which is where the README places its blacklist example: it compares ngx.var.remote_addr against a fixed address and calls ngx.exit(ngx.HTTP_FORBIDDEN). Code in content_by_lua generates the response itself, as in the /lua_content example where a single ngx.say('Hello,world!') is the whole handler.

The data flow is therefore not a new server. Requests still enter NGINX, still go through the normal location matching, and still leave through the normal upstream path. The Lua handlers are inserted at named points, and they reach NGINX internals through the ngx.* API: ngx.var for variables, ngx.req for the request object, ngx.location.capture for subrequests. The README's /lua example uses capture to run an internal subrequest and read back res.status and res.body, which is how Lua code composes internal endpoints without a network round trip.

## Installing lua-nginx-module and running a first handler

Because the module is not part of the NGINX source, installation means either building it against an NGINX source tree or using OpenResty, which the README points to at openresty.org and treats as the normal path. The README has an Installation section with a subsection on building as a dynamic module, and a separate C Macro Configurations subsection for compile-time switches.

The README does not reproduce an install command line in the excerpt available here, so the honest statement is: follow the Installation section of the repository README, which covers the dynamic module build, or install OpenResty from openresty.org and get the module already compiled in. Do not assume a distro package named after this repository exists.

Once the module is present, the smallest useful configuration is a location whose response is produced by Lua. This is adapted from the README synopsis:

```nginx
location /lua_content {
    default_type 'text/plain';

    content_by_lua_block {
        ngx.say('Hello,world!')
    }
}
```

A request to /lua_content returns the text Hello,world! with the content type set by default_type. The block form keeps the Lua inline in nginx.conf, which is convenient for short handlers.

The next step is reading request data. The README's /nginx_var example shows ngx.var.arg_a, which is the query parameter a:

```nginx
location /nginx_var {
    default_type 'text/plain';

    content_by_lua_block {
        ngx.say(ngx.var.arg_a)
    }
}
```

A request to /nginx_var?a=hello,world prints hello,world. For anything longer than a few lines, the README's /mixed example shows the file-based form:

```nginx
location = /mixed {
    rewrite_by_lua_file /path/to/rewrite.lua;
    access_by_lua_file /path/to/access.lua;
    content_by_lua_file /path/to/content.lua;
}
```

Note the caution the README attaches to building file paths from NGINX variables: contents in an nginx var must be carefully filtered, otherwise there is a security risk. The example that interpolates $path into content_by_lua_file is a pattern to treat as dangerous unless the variable is strictly validated.

## Known Issues is the section that decides your architecture

The README carries a Known Issues section with subsections that are not trivia. They are constraints that change what you can write.

Lua coroutine yielding and resuming is listed, which matters because Lua code inside NGINX is not plain Lua. Lua variable scope is listed as well, and that one bites quietly: a variable you expect to be local to one request can behave differently depending on where it is declared, because the Lua VM is shared across requests in a worker. Data sharing within an Nginx worker has its own section, which tells you that the module provides worker-level sharing rather than a global store.

Cosockets are listed as not available everywhere. That is a real design limit: outbound non-blocking connections cannot be opened from every phase, so a plan that assumes "call the auth service from wherever the code happens to run" may not work. The README also notes that mixing with SSI is not supported and that SPDY mode is not fully supported, and it flags missing data on short circuited requests.

There is also a TCP socket connect operation issue documented. Read together, these constraints say the module is production ready, as the Status section states, but it is production ready within a defined execution model. Design against that model rather than discovering it in staging.

## Where lua-nginx-module is the wrong tool

If your logic is a normal web application, this module is the wrong layer. Routing, templating, database access and business rules belong in an application server where you have a debugger, a test runner and no need to recompile a C module. The README's own examples are short: a greeting, a query parameter, a body read, a subrequest. That scale is the hint.

Second, if you cannot rebuild or replace NGINX, you cannot adopt this cleanly. The module is not distributed with the NGINX source, so a managed NGINX service that only accepts configuration files will not run it. You need either a custom build or OpenResty.

Third, if your team does not write Lua, the cost is not the language. It is the phase model, the ngx.* API and the Known Issues list. A team that will not read those will write code that appears to work and then fails under concurrency.

Finally, do not use it as a general-purpose cache or queue. The README describes data sharing within an Nginx worker, not across workers or hosts, so any design that assumes a single shared table across the whole server is built on a false premise.

## OpenResty versus assembling the module yourself

The real alternative is not another Lua module. It is OpenResty as a distribution. The README states plainly that this module is a core component of OpenResty and that if you are using it, you are essentially using OpenResty. That framing is accurate: OpenResty packages NGINX plus this module plus the surrounding Lua libraries into one build.

The difference in approach is who owns the build. Taking the module alone means you track NGINX compatibility yourself, choose between a static and a dynamic module build, and set any C macro configurations you need. Taking OpenResty means you accept its release cadence and its bundled versions in exchange for not maintaining that toolchain.

There is a middle option the README supports directly: building as a dynamic module. That keeps your existing NGINX package and loads the module separately, which is attractive when you already have NGINX configured the way you want and only need the Lua hooks. It also means the module and the NGINX binary must stay compatible across upgrades, which is a maintenance item rather than a one-time decision.

What none of these options give you is a different execution model. Whether you install the module alone or through OpenResty, the phase hooks, the ngx.* API and the Known Issues are the same.

## Maintenance cost, versioning and licence status

The repository is not archived and the last push was on 2026-09-17, so it is being worked on. The README describes version v0.10.29, released on Oct 24, 2025, and the document is versioned alongside the code, which means an upgrade can change documented behaviour and not just the binary.

Upgrade cost has two parts. The module must match your NGINX version, and the README has an Nginx Compatibility section that is the place to check before moving either one. Then there is LuaJIT: the README has sections on LuaJIT bytecode support and on statically linking pure Lua modules, which tells you bytecode and module loading are part of the operational surface. Precompiled bytecode is an option the README documents, not a default you inherit.

On licensing, this review cannot give you a definitive answer. The repository metadata available here lists the licence as unknown, while the README has a Copyright and License section. Read that section in the repository before you ship, and if you redistribute a build, confirm the terms that apply to the module and to LuaJIT separately. That is a question for your own legal review, not something to infer from a directory listing.

## Conclusion

Adopt lua-nginx-module when you need request-time logic inside NGINX itself: per-request auth, header rewriting, body inspection or upstream fan-out in Lua. Skip it if you only need static serving or a reverse proxy with no scripting, because it adds a C module build and a LuaJIT dependency you would otherwise not carry. Before committing, verify your NGINX version against the compatibility table in the README, confirm whether you will build as a dynamic module or take the OpenResty bundle, and check the Known Issues section for the coroutine and cosocket restrictions that apply to your design.

## FAQ

### What is the main purpose of Lua in the lua-nginx-module?

In this module Lua is the scripting language embedded into NGINX HTTP servers, and it runs at request phases such as rewrite, access and content. The README describes the module as a core component of OpenResty, so using OpenResty means you are essentially using it.

### What is the lua-nginx-module?

It is ngx_http_lua_module, which embeds Lua into NGINX HTTP servers. The README states that it is not distributed with the Nginx source and points to its own installation instructions.

### How do you install the lua-nginx-module?

The README states that the module is not distributed with the NGINX source and points to its Installation section, which includes a subsection on building it as a dynamic module. The other route it names is OpenResty at openresty.org, where the module is already part of the distribution.

### Is there a lua-nginx-module example I can copy?

Yes. The README synopsis includes a location using content_by_lua_block with ngx.say('Hello,world!'), a location reading ngx.var.arg_a from the query string, and a /mixed location that combines rewrite_by_lua_file, access_by_lua_file and content_by_lua_file.

### Where is the lua-nginx-module documentation?

The README in the openresty/lua-nginx-module repository is the documentation, and it is long: it covers directives, the Nginx API for Lua, Known Issues and build instructions. The document is versioned, describing v0.10.29 released on Oct 24, 2025.

## Sources

- [Issues](https://github.com/openresty/lua-nginx-module/issues)
- [openresty/lua-nginx-module on GitHub](https://github.com/openresty/lua-nginx-module)
- [Project website](https://openresty.org/)
- [README](https://github.com/openresty/lua-nginx-module/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/openresty-lua-nginx-module
