Overview
ngx_mruby enables you to run mruby scripts on nginx. For example, you can implement:
- your own auth mechanism like nginx auth_basic
- reverse proxy with your own backend selection algorithm
- content handler that returns any HTTP response
See use cases page for more examples. The scripts can be invoked at any of nginx HTTP phases. See nginx phases doc for more details of the phases.
ngx_mruby provides 2 modules. One is HTTP module for HTTP, the other is Stream module for TCP/UDP stream. See HTTP module directives and Stream module directive section for more details.
Here is a simple example of a content handler for HTTP.
nginx.conf
Section titled “nginx.conf”location /hello { mruby_content_handler /path/to/hello.rb;}/path/to/hello.rb
Section titled “/path/to/hello.rb”Nginx.echo "Hello World"$ curl http://127.0.0.1/helloHello World‘mruby_
‘Nginx.echo’ is a method to creates a response body. ngx_mruby provides classes for accessing or manipulating nginx internal data structures. See classes for ngx_mruby for more information.
Caching compile code
Section titled “Caching compile code”By default, mruby script is compiled whenever it is invoked. You can enable compiled code cache with ‘cache’ argument for performance.
nginx.conf
Section titled “nginx.conf”location /hello { mruby_content_handler /path/to/hello.rb cache;}Inline code
Section titled “Inline code”You can write mruby code in nginx.conf instead of creating a script file. The compiled code is cached.
nginx.conf
Section titled “nginx.conf”location /hello { mruby_content_handler_code ' Nginx.echo "Hello World" ';}Nginx variables
Section titled “Nginx variables”You can set nginx variable by a mruby script. The following example set a value returned from ‘proxy.rb’ to the variable ‘$backend’.
nginx.conf
Section titled “nginx.conf”location /proxy { mruby_set $backend /path/to/proxy.rb;}Also you can enable caching, or write inline mruby code as same as other directives.
nginx.conf
Section titled “nginx.conf”location /proxy { mruby_set $backend /path/to/proxy.rb cache;}nginx.conf
Section titled “nginx.conf”location /proxy { mruby_set_code $backend ' backends = [ "test1.example.com", "test2.example.com", "test3.example.com", ] backends[rand(backends.length)] ';}Hooks on initialization phases
Section titled “Hooks on initialization phases”ngx_mruby provides hooks invoked on several nginx initialization phases as below. See nginx Core Modules doc for more about initialization phases.
http { mruby_init /path/to/init.rb; mruby_init_worker /path/to/init_worker.rb; mruby_exit_worker /path/to/exit_worker.rb; server { location / { mruby_content_handler /path/to/handler.rb; } }}Script file as a handler
Section titled “Script file as a handler”You can run a ruby script file directly without location definition. In the following example, the script is exposed at http://127.0.0.1/YOUR_MRUBY_SCRIPT.rb
location ~ \.rb$ { mruby_add_handler on;}HTTP module directives
Section titled “HTTP module directives”- mruby_init and mruby_
init_ code - mruby_
init_ worker and mruby_ init_ worker_ code - mruby_
exit_ worker and mruby_ exit_ worker_ code - mruby_
ssl_ handshake_ handler and mruby_ ssl_ handshake_ handler_ code - mruby_
add_ handler - mruby_
post_ read_ handler and mruby_ post_ read_ handler_ code - mruby_
server_ rewrite_ handler, mruby_ server_ rewrite_ handler_ code, mruby_ rewrite_ handler and mruby_ rewrite_ handler_ code - mruby_
access_ handler and mruby_ access_ handler_ code - mruby_
content_ handler and mruby_ content_ handler_ code - mruby_
log_ handler and mruby_ log_ handler_ code - mruby_set and mruby_
set_ code - mruby_
output_ body_ filter, mruby_ output_ body_ filter_ code, mruby_ output_ header_ filter and mruby_ output_ header_ filter_ code - ruby_
enable_ read_ request_ body - mruby_
upstream_ keepalive
TODO
- Add missing directives
- mruby_
server_ context_ handler_ code - mruby_cache
- mruby_
- Add examples
mruby_init and mruby_init_code
Section titled “mruby_init and mruby_init_code”Syntax: mruby_init /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: httpPhase: initialize master processSyntax: mruby_init_code 'YOUR MRUBY CODE HERE';Default: -Context: httpPhase: initialize master process‘mruby_init’ and ‘mruby_
mruby_init_worker and mruby_init_worker_code
Section titled “mruby_init_worker and mruby_init_worker_code”Syntax: mruby_init_worker /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: httpPhase: initialize worker processSyntax: mruby_init_worker_code 'YOUR MRUBY CODE HERE';Default: -Context: httpPhase: initialize worker processAfter initializing nginx master process, it creates worker processes.
‘mruby_
The code runs outside any request.
Nginx.log writes to nginx’s error log here (since 2.7.1; 2.7.0 raised a RuntimeError).
Request-scoped methods such as Nginx::Request, Nginx::Var and Nginx.rputs raise a RuntimeError here (since 2.7.1; in 2.7.0 they crashed the worker).
See Nginx.errlogger for what code outside a request can call.
mruby_exit_worker and mruby_exit_worker_code
Section titled “mruby_exit_worker and mruby_exit_worker_code”Syntax: mruby_exit_worker /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: httpPhase: exit worker processSyntax: mruby_exit_worker_code 'YOUR MRUBY CODE HERE';Default: -Context: httpPhase: exit worker process‘mruby_
The code runs outside any request.
Nginx.log writes to nginx’s error log here (since 2.7.1).
Request-scoped methods such as Nginx::Request, Nginx::Var and Nginx.rputs raise a RuntimeError here (since 2.7.1; in 2.7.0 they used the last request that the worker had handled, or crashed the worker when it had handled none).
See Nginx.errlogger for what code outside a request can call.
mruby_ssl_handshake_handler and mruby_ssl_handshake_handler_code
Section titled “mruby_ssl_handshake_handler and mruby_ssl_handshake_handler_code”Syntax: mruby_ssl_handshake_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: serverPhase: SSL handshakeSyntax: mruby_ssl_handshake_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: serverPhase: SSL handshake‘mruby_
The code runs for a TLS connection, before any request on it, and uses Nginx::Nginx::SSL.log writes to the log of the connection, and Nginx.log writes to nginx’s error log (since 2.7.1).
Request-scoped methods such as Nginx::Request, Nginx::Var and Nginx.rputs raise a RuntimeError here (since 2.7.1; in 2.7.0 they used the last request that the worker had handled, or crashed the worker when it had handled none).
See Nginx.errlogger for what code outside a request can call.
mruby_add_handler
Section titled “mruby_add_handler”Syntax: mruby_add_handler on | off;Default: offContext: server, locationPhase: initialize master processWhen set to the value on, mruby scripts are added as handlers. You can run the script files directly without endpoint definition.
mruby_post_read_handler and mruby_post_read_handler_code
Section titled “mruby_post_read_handler and mruby_post_read_handler_code”Syntax: mruby_post_read_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: serverPhase: NGX_HTTP_POST_READ_PHASESyntax: mruby_post_read_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: serverPhase: NGX_HTTP_POST_READ_PHASE‘mruby_
mruby_server_rewrite_handler, mruby_server_rewrite_handler_code, mruby_rewrite_handler and mruby_rewrite_handler_code
Section titled “mruby_server_rewrite_handler, mruby_server_rewrite_handler_code, mruby_rewrite_handler and mruby_rewrite_handler_code”Syntax: mruby_server_rewrite_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase: NGX_HTTP_SERVER_REWRITE_PHASESyntax: mruby_server_rewrite_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase: NGX_HTTP_SERVER_REWRITE_PHASESyntax: mruby_rewrite_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase: NGX_HTTP_REWRITE_PHASESyntax: mruby_rewrite_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase: NGX_HTTP_REWRITE_PHASEThe rewrite handlers are mainly used for URL rewrite.
‘mruby_
TODO: I’m not sure if the above contexts are correct.
mruby_access_handler and mruby_access_handler_code
Section titled “mruby_access_handler and mruby_access_handler_code”Syntax: mruby_access_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase: NGX_HTTP_ACCESS_PHASESyntax: mruby_access_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase: NGX_HTTP_ACCESS_PHASE‘mruby_
mruby_content_handler and mruby_content_handler_code
Section titled “mruby_content_handler and mruby_content_handler_code”Syntax: mruby_content_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase: NGX_HTTP_CONTENT_PHASESyntax: mruby_content_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase: NGX_HTTP_CONTENT_PHASEmruby_
mruby_log_handler and mruby_log_handler_code
Section titled “mruby_log_handler and mruby_log_handler_code”Syntax: mruby_log_handler /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase: NGX_HTTP_LOG_PHASESyntax: mruby_log_handler_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase: NGX_HTTP_LOG_PHASE‘mruby_
mruby_set and mruby_set_code
Section titled “mruby_set and mruby_set_code”Syntax: mruby_set $VARNAME /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase: NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_REWRITE_PHASESyntax: mruby_set_code $VARNAME 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase: NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_REWRITE_PHASE‘mruby_set’ and ‘mruby_
If the code raises an exception before it waits in Nginx::Async.sleep or Nginx::Async::HTTP.sub_request, ngx_mruby logs it, the variable is set to an empty string (as when the code returns nil), also when an earlier directive of the location gave it a value, and the request goes on.
Text that the code wrote with Nginx.rputs or Nginx.echo before the exception is not sent and does not count in the Content-Length of the response.
If the code raises after such a call returned, the request gets 500 and the output of Nginx.rputs is not sent, as described under Nginx::
mruby_output_body_filter, mruby_output_body_filter_code, mruby_output_header_filter and mruby_output_header_filter_code
Section titled “mruby_output_body_filter, mruby_output_body_filter_code, mruby_output_header_filter and mruby_output_header_filter_code”Syntax: mruby_output_body_filter /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase:Syntax: mruby_output_body_filter_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase:Syntax: mruby_output_header_filter /PATH/TO/YOUR/MRUBY/SCRIPT.rb [cache];Default: -Context: server, locationPhase:Syntax: mruby_output_header_filter_code 'YOUR MRUBY CODE HERE';Default: -Context: server, locationPhase:The filters are used for processing a response. The header filters are for HTTP header, the body filters are for HTTP body.
A body filter reads the response body with Nginx::Filter#body and can replace it with Nginx::Filter#body= (or #output).
The response then has exactly that body, and Content-Length is its length.
Since 2.7.1 this also holds when the body was written with Nginx.rputs or Nginx.echo; in 2.7.0 such a response kept the length that Nginx.rputs had produced, so a replacement body of another length was cut or padded.
A header filter runs before the body has been read.
There Nginx::Filter#body returns an empty string and Nginx::Filter#body= raises a RuntimeError (both since 2.7.1).
Nginx::Async.sleep and Nginx::Async::HTTP.sub_request raise a RuntimeError in both kinds of filter (since 2.7.1), because nginx cannot resume a filter that waits.
mruby_enable_read_request_body
Section titled “mruby_enable_read_request_body”Syntax: mruby_enable_read_request_body on | off;Default: offContext: server, locationPhase: initialize master processWhen set to the value on, ngx_mruby reads a HTTP request body before calling mruby code.
Due to nginx event driven nature, a request body is not always read when invoking a handler like mruby_
When the body is not complete yet, a post-read, server rewrite, rewrite, access or content handler runs once nginx has read the rest of it, and the request then goes on with the later phases, also with a phase handler that waits for a subrequest, such as auth_request.
If the client closes the connection or client_body_timeout expires first, nginx ends the request and the handler does not run; after client_body_timeout nginx closes the connection without a response, and the access log records status 408.
Before 2.7.1, a server rewrite, rewrite or content handler whose body was not complete never ran, and the client got no response; after a post-read or access handler had waited for the body, a later phase handler that waits for a subrequest, such as auth_request, was not resumed.
mruby_set and mruby_set_code handlers, log handlers and header and body filters cannot wait for the rest of the body, because nginx goes on as soon as they return.
They see the body only when it is complete before they run: when an earlier handler or module has read it, or, for HTTP/1.x with a Content-Length header, when the whole body is in the buffer that nginx read the request headers into (client_header_buffer_size, or a buffer of large_client_header_buffers when the headers needed one).
A chunked body, an HTTP/2 or HTTP/3 body, and a body of which only a part is in that buffer count as not complete, even when the rest has already arrived.
Otherwise they run without the body (Nginx::Request#body returns nil), and ngx_mruby logs that at the info level.
A body that a post-read, server rewrite, rewrite, access or content handler is still waiting for does not count as complete either: when nginx ends that read without the rest of the body (the client closes the connection, client_body_timeout expires, or nginx answers 400 or 413 to the body), a log handler or a filter that runs afterwards runs at once, without waiting for the rest, and the waiting handler does not run.
Nginx::Request#body then returns the part of the body that nginx had read before it ended the read, or nil when that part did not fit in client_body_buffer_size and nginx had written it to a temporary file.
A later handler that can wait, or a module such as proxy_pass, still reads the whole body.
To give these handlers the whole body, read it first in a handler that can wait and runs before them, such as a server-level mruby_post_read_handler_code.
Before 2.7.1, such a handler did not run its code when the body was not complete, and the body read that it had started ran the phases of the request again when it completed (since 2.7.1 it runs at once, without waiting for the rest of the body, as described above).
mruby_upstream_keepalive
Section titled “mruby_upstream_keepalive”Syntax: mruby_upstream_keepalive connections;Default: -Context: upstreamPhase: initialize master processActivates the cache for connections to upstream servers.
It’s same as ngx_
TODO: describe the difference between keepalive and mruby_
Stream module directives
Section titled “Stream module directives”ngx_mruby also supports TCP/UDP stream. The stream module directives are similar to The HTTP module directives. Here are examples of the stream module directives.
nginx.conf
Section titled “nginx.conf”server { mruby_stream /path/to/stream.rb;}nginx.conf
Section titled “nginx.conf”stream { upstream dynamic_server0 { server 127.0.0.1:58080; }
server { listen 12346; mruby_stream_code ' c = Nginx::Stream::Connection.new "dynamic_server0" c.upstream_server = "192.168.0.3:54321" Nginx::Stream.log Nginx::Stream::LOG_NOTICE, "dynamic_server0 was changed to 192.168.0.3:54321" '; proxy_pass dynamic_server0; }}nginx.conf
Section titled “nginx.conf”stream { upstream dynamic_server0 { server 127.0.0.1:58080; }
mruby_stream_init_code ' Userdata.new.new_upstream = "127.0.0.1:58081" ';
mruby_stream_init_worker_code ' p "ngx_mruby: STREAM: mruby_stream_init_worker_code" ';
mruby_stream_exit_worker_code ' p "ngx_mruby: STREAM: mruby_stream_exit_worker_code" ';
server { listen 12346; mruby_stream_code ' c = Nginx::Stream::Connection.new "dynamic_server0" c.upstream_server = Userdata.new.new_upstream '; proxy_pass dynamic_server0; }TODO
- Add descriptions to the above examples
- Add directive references like HTTP module