Skip to content

Filters

nginx sends the header and the body of a response through two chains of filters, whichever handler or upstream produced the response. ngx_mruby adds one filter to each chain: mruby_output_header_filter (and its form _code) runs Ruby on the header, and mruby_output_body_filter (and _code) on the body. ngx_mruby adds its header filter when the configuration has either directive, and its body filter when it has a body filter.

nginx's header filters and body filters in the order in which nginx calls them, with the filters of ngx_mruby, and what each filter of ngx_mruby receives.Drawn from these lines of ngx_mruby at 0c72e3970 (2026-10-19) and of nginx:
  • src/http/ngx_http_mruby_module.c lines 591 to 595, 1676 to 1677, 1955 to 1965, 2041 to 2054, 2085 to 2122 and 2135 to 2154
  • config.in lines 48 to 69
  • auto/modules lines 165 to 187, 386 to 427 and 1494 to 1495 of nginx 1.31.6
  • auto/module lines 23 to 30 of nginx 1.31.6
  • nginx calls the filters of each chain from the top. ngx_mruby’s filters come after nginx’s not-modified header filter and its range and copy body filters, and before the filters of nginx’s other modules, such as gzip and the chunked filter, so a filter of ngx_mruby sees the response before nginx compresses or chunks it. nginx builds the chains in the order of its modules (auto/modules), where ngx_mruby stands after nginx’s own filter modules and before the copy, range body, not-modified and slice filters, and each filter goes in front of those before it. The slice filter exists in a build with --with-http_slice_module.
  • The header filter reads and changes the status and the headers through Nginx::Request and Nginx::Headers_out. It runs before the body has been read: there Nginx::Filter#body returns an empty string.
  • The body filter needs the length of the response. For a response with Content-Length, ngx_mruby collects the whole body into one buffer, runs the script once with it in Nginx::Filter#body, and sends what the script puts into Nginx::Filter#body=. A response without Content-Length, such as a chunked response of an upstream, passes the filter unread, and ngx_mruby logs that at the notice level.
  • When a location has a body filter, ngx_mruby holds the header back until the body filter has run, so that Content-Length is the length of the new body.
  • Neither filter can wait: Nginx::Async.sleep and Nginx::Async::HTTP.sub_request raise a RuntimeError there, and Nginx.rputs and Nginx.echo cannot be used.

The guide Change a response body has a sample of both filters that CI runs. The directive reference gives their syntax.