Skip to content

Deny clients listed in a file

An access handler runs in nginx’s access phase, before the content of the location. A Ruby script there can end the request with a status, for example 403, or let it go on by returning without a status (mruby_access_handler).

The access handler reads the file that the variable $deny_list names and answers 403 when the client’s address is one of its lines. The location /listed/ names a list that holds 127.0.0.1, the address of the test client, so the request gets 403; error_page 403 @denied gives that 403 its body. The location /open/ names a list without that address.

test/conf/conf.d/74-guide-access.conf
server {
listen 18162;
default_type text/plain;
error_page 403 @denied;
# The list of this location holds 127.0.0.1, the address of the case.
location /listed/ {
set $deny_list build/nginx/html/guide_access_listed.txt;
mruby_access_handler build/nginx/html/guide_access.rb;
mruby_content_handler_code 'Nginx.rputs "welcome\n"';
}
# The list of this location does not hold it.
location /open/ {
set $deny_list build/nginx/html/guide_access_open.txt;
mruby_access_handler build/nginx/html/guide_access.rb;
mruby_content_handler_code 'Nginx.rputs "welcome\n"';
}
location @denied {
return 403 "your address is on the deny list\n";
}
}
test/html/guide_access.rb
r = Nginx::Request.new
path = r.var.deny_list
listed = File.exist?(path) && File.read(path).split("\n").include?(r.var.remote_addr)
Nginx.return Nginx::HTTP_FORBIDDEN if listed
test/html/guide_access.request: the request that the case sends
GET /listed/report HTTP/1.1
Host: 127.0.0.1:18162
Connection: close
test/html/guide_access.response: the response that the case compares
HTTP/1.1 403 Forbidden
Content-Type: text/plain
your address is on the deny list

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 case also sends GET /open/report and expects 200 with the body welcome.

  • The script reads the list file for every request, which blocks the worker while the disk answers. To read it once per worker, load the list in mruby_init_worker_code into a constant, and reload nginx after a change of the file (What a worker keeps between requests).
  • r.var.remote_addr is the address of the TCP peer. Behind a load balancer, that is the load balancer, unless nginx’s realip module sets it from a header that the load balancer sends.
  • Nginx.return Nginx::HTTP_FORBIDDEN ends the request with 403. A script that returns without Nginx.return lets nginx go on with the next handler of the access phase and then the content.