Skip to content

Change a response body

A body filter runs while nginx sends a response, whichever handler or upstream produced it. With mruby_output_body_filter, a Ruby script reads the body through Nginx::Filter#body and replaces it with Nginx::Filter#body=. A header filter (mruby_output_header_filter) runs before it and can change the response headers.

The location sends a text file from disk. The header filter sets the content type to text/html, and the body filter escapes &, < and > and wraps the text in an HTML page.

test/conf/conf.d/76-guide-body-filter.conf
server {
listen 18167;
# A text file from disk, sent as an HTML page.
location = /notes {
alias html/guide_notes.txt;
mruby_output_header_filter_code 'Nginx::Request.new.content_type = "text/html"';
mruby_output_body_filter build/nginx/html/guide_body_filter.rb;
}
}
test/html/guide_body_filter.rb
f = Nginx::Filter.new
text = f.body.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
f.body = "<!doctype html>\n<title>notes</title>\n<pre>\n#{text}</pre>\n"
test/html/guide_body_filter.request: the request that the case sends
GET /notes HTTP/1.1
Host: 127.0.0.1:18167
Connection: close
test/html/guide_body_filter.response: the response that the case compares
HTTP/1.1 200 OK
Content-Type: text/html
<!doctype html>
<title>notes</title>
<pre>
Notes from disk.
The filter escapes &lt;b&gt; and &amp;.
</pre>

On every CI run, the case test/t/cases/guides.rb sends this request and compares the status line, the headers shown on this page and the whole body. The five files last changed on 2026-10-29 in commit 32219e53.

  • The body filter reads a response of known length, such as a file from disk, as one buffer. nginx passes a response without Content-Length on without the filter reading it (Nginx::Filter).
  • ngx_mruby sends the length of the new body in Content-Length.
  • The use case “Output filter converting Markdown to HTML” of Use cases converts Markdown in the same place, with the gem mruby-discount.