A simple and extensible HTTP server framework for OpenResty.
Switch branches/tags
Nothing to show
Clone or download
Pull request Compare This branch is 21 commits ahead, 1 commit behind kidd:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



A simple and extensible HTTP server framework for OpenResty, providing a clean method for loading Lua HTTP applications ("resty" modules) into Nginx.

Drawing inspiration from Rack and also Connect, lua-resty-rack allows you to load your application as a piece of middleware, alongside other middleware. Your application can either; ignore the current request, modify the request or response in some way and pass on to other middleware, or take responsibiliy for the request by generating a response.


This library is considered experimental and the API may change without notice. Please feel free to offer suggestions or raise issues here on Github.


Copy the rack.lua file inside your nginx's lib folder and make sure it is included in the lua_package_path variable in nginx.conf.

Quick example

To install middleware for a given location, you simply call rack.use(middleware) in the order you wish the modules to run, and then finally call rack.run().

server {
  location / {
    content_by_lua '
      local rack = require "rack"
      local r    = rack.new()

      r:use(require "my.middleware")
      local response = r:run()


local rack = require 'rack'
local r    = rack.new()

This is how you create a rack instance. The rest of the methods in rack require instances.

r:use(middleware, ...)

r is a rack instance created with rack.new().

The middleware parameter must be a callable object (usually a Lua function).

The function should accept at least two params:req and next_middleware. req is the request object (see below). next_middleware, when executed, will invoke the next middleware in the pipeline and will return a res (response) object.

All middleware functions should at least return a response. It is recommended to use res.

r:use(function(req, next_middleware)
  res.headers["X-Homer"] = "D'oh!"
  return next_middleware()

Any extra arguments passed to r:use will be passed to the middleware after req and res.

local replacer_mw = function(req,next_middleare,from,to)
  local res = next_middleware()
  res.body = res.body:gsub(from, to)
  return res

r:use(function(req, next_middleware)
  local res = next_middleware()
  res.status = 200
  res.body   = "I like Chocolate"
  return res

r:use(body_replacer, "Chocolate", "Vanilla")

It's possible to chain more than one middleware by calling r:use several times. The middlewares will be executed in the same order they are included by r:use. They can modify req and res by changing their properties or adding new ones. It is required that at least res.status is set to something by at at least one of the middlewares.

local response = r:run([req])

r is a rack instance created with rack.new().

When the optional parameter req is specified, it is used as the request. If no request is specified, a default request will be created using r:create_initial_request().

r:run() executes each of the middlewares in order, until the list is finished or one of the middlewares stops the pipeline (see below).

response is the result of applying all the middlewares in order to the initial response (which has the values listed below).

Middlewares will be executed in the same order as they were included by r:use().

Each middleware can make modifications to req and res, and the next middleware will receive them (res must be returned). The rest of parameters are optional and will be the same ones provided in r:use().

The middleware pipeline can be halted by any middleware who whishes to do so, by not calling next_middleware.

Note that res.status is mandatory. Attempting to halt the pipeline without setting it will result in an error. If res.status is "valid" (for example, 200), then res.body must be set to a non-empty string.

local req = r:create_initial_request()

r is a rack instance created with rack.new().

req is a table with the properties defined below, in the req section.

If no request object is passed to r:run(), it will create one using r:create_initial_request().


Sends the response to the server. Usually response was returned by r:run.

r:run and r:respond are separated so that further actions can be done in the server after running the middlewares but before sending the response back (for example, handling errors or storing the final response on a database). If no such treatment is needed, the following one-liner can be used:


Or, if you want to use your own request,


res attributes


The HTTP status code to return. There are constants defined for common statuses. This value must be set by at least one middleware, otherwise the execution of r:run will result in an error.


A table containing the request headers. Keys are matched case insensitvely, and optionally with underscores instead of hyphens. e.g.

req.headers["X-Foo"] = "bar"
res.body = req.headers.x_foo --> "bar"


The response body. It's initially empty, and will be returned by nginx as response after the last middleware in the chain has been executed.

req attributes

Note that if you pass your own req parameter to r:run, the following will not be valid (whatever you pass will be used as a parameter)


The HTTP method, e.g. GET, set from ngx.var.request_method.


The protocol scheme http|https, set from ngx.var.scheme.


e.g. /my/uri, set from ngx.var.uri.


The hostname, e.g. example.com, set from ngx.var.host.


The querystring, e.g. var1=1&var2=2, set from ngx.var.query_string.


The query args, as a table, set from ngx.req.get_uri_args().


A table of headers. Just like in res.header, keys are normalized before being matched.


The request body. It's loaded automatically (via metatables) the first time it's requested, since it's an expensive operation. Then it is cached. It can also be set to anything else by any middleware.


In order to execute the tests, run the following command:


The tests assume that you have PERL and openresty installed.

You might need to edit the Makefile to point it to your openresty folder.


Licensed under the 2-clause BSD license. See BSD-LICENSE.md for details.