Joshua.Hu | Joshua Rogers' Scribbles

nginx 'allow' and 'deny' directives with 'return'

Another popular configuration I’ve found in nginx configurations is as follows:

location /a_folder/ {
  allow 127.0.0.1;
  deny all;
  return 200 "This is my secret folder!";
}

The problem is, this doesn’t work as one would expect. During nginx’s runtime, the first “stage” of a request is its rewrite phase (source). This rewrite phase is responsible for the directives break, if, return, rewrite, rewrite_log, set, and uninitalized_variable_warn. The famous saying in nginx is “if is evil”. But what about return? Indeed, return can be evil, too.

In the above example, first the return is evaluated in the rewrite stage, and then in theory, the allow and deny directives are evaluated (in the ngx_http_access_module module). However, since return.. returns, the access directives are never evaluated, and the endpoint will always return what is set, no matter the intention of the configuration.

In order to do what is intended, the correct configuration is:

location /a_folder/ {
  allow 127.0.0.1;
  deny all;

  try_files "" @secret_msg;
}

location @secret_msg {
  internal;
  default_type text/plain;
  return 200 "This is my secret folder!";
}