"index off;" directive?

Maxim Dounin mdounin at mdounin.ru
Tue May 5 09:16:15 UTC 2026


Hello!

On Sun, May 03, 2026 at 10:22:16PM -0300, Fabiano Furtado wrote:

> On Thu, Apr 30, 2026 at 12:34 AM Maxim Dounin wrote:
> >
> > On Tue, Apr 28, 2026 at 08:32:32PM -0300, Fabiano Furtado wrote:
> >
> > > On Mon, Apr 27, 2026 at 9:04 PM Maxim Dounin wrote:
> > > >
> > > > On Mon, Apr 27, 2026 at 09:22:59 AM -0300, Fabiano Furtado wrote:
> > > >
> > > > > On Sun, Apr 26, 2026 at 7:53 PM Maxim Dounin wrote:
> > > > > > ...
> > > > > Anyway, I'm not sure if it's possible, but I'd like to try developing
> > > > > this patch. Would it be okay if I tried to create this new patch?
> > > >
> > > > Yep, feel free to.
> > > >
> > > > Just in case, some hints can be found here:
> > > > https://freenginx.org/en/docs/contributing_changes.html
> > >
> > > This patch introduces the "index off;" directive for freenginx, but I
> > > also had to modify other parts of the code to ensure this new
> > > directive behaves correctly.
> >
> > Please make sure to read the above link.  In particular, following
> > the coding style and using Mercurial is highly recommended, as it
> > makes it much easier to review the code.
> >
> > > When "off" is applied in the config and an HTTP access occurs, a
> > > message appears in the error_log: "directory index of ".../" is
> > > forbidden". In this context, I believe this message is not
> > > particularly relevant for the error_log. So, I added a flag to the
> > > core called "log_forbidden", which works similarly to "log_not_found".
> >
> > I believe this change is completely orthogonal and has nothing to
> > do with the "index off;".  The message is perfectly identical with
> > both "index off;" and the "index .non_such_file;" workaround, so
> > "index off;" introduces nothing new here.  Also, the message can
> > be suppressed with "location ~ /$ { return 403; }" if no indexing
> > is indeed desired.
> >
> > That is, if at all, this should be a separate patch, explaining
> > the change and reasons for it.
> >
> > > As a result, instead of modifying only "ngx_http_index_module.c", I
> > > ended up changing four files:
> > >   * src/http/ngx_http_core_module.c
> > >   * src/http/ngx_http_core_module.h
> > >   * src/http/modules/ngx_http_access_module.c
> > >   * src/http/modules/ngx_http_index_module.c
> > >
> > > I would appreciate it if the code could be reviewed. Any feedback or
> > > suggestions are welcome.
> > >
> > > Thank you in advance for the opportunity to submit this patch.
> > > Fabiano Furtado
> >
> > > # diff -Nau freenginx-1.30.0/src/http/ngx_http_core_module.c freenginx-1.30.0_patched/src/http/ngx_http_core_module.c >ngx_http_core_module.c.patch
> > > --- freenginx-1.30.0/src/http/ngx_http_core_module.c  2026-04-14 05:23:27.000000000 -0300
> > > +++ freenginx-1.30.0_patched/src/http/ngx_http_core_module.c  2026-04-28 19:56:18.478687257 -0300
> > > @@ -628,7 +628,14 @@
> > >        offsetof(ngx_http_core_loc_conf_t, msie_refresh),
> > >        NULL },
> > >
> > > -    { ngx_string("log_not_found"),
> > > +    { ngx_string("log_forbidden"),
> > > +      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
> > > +      ngx_conf_set_flag_slot,
> > > +      NGX_HTTP_LOC_CONF_OFFSET,
> > > +      offsetof(ngx_http_core_loc_conf_t, log_forbidden),
> > > +      NULL },
> > > +
> > > +   { ngx_string("log_not_found"),
> >
> > Note that "log_not_found" in diff suggests there is a white space
> > damage.  And, indeed, the line lost one of the leading spaces.
> >
> > Also, in many cases it is a good idea to define new directives
> > after the one you want to mimic, and not before it.
> >
> > >        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
> > >        ngx_conf_set_flag_slot,
> > >        NGX_HTTP_LOC_CONF_OFFSET,
> > > @@ -1177,7 +1184,8 @@
> > >  ngx_http_core_post_access_phase(ngx_http_request_t *r,
> > >      ngx_http_phase_handler_t *ph)
> > >  {
> > > -    ngx_int_t  access_code;
> > > +    ngx_int_t                  access_code;
> > > +    ngx_http_core_loc_conf_t  *clcf;
> > >
> > >      ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
> > >                     "post access phase: %ui", r->phase_handler);
> > > @@ -1187,7 +1195,9 @@
> > >      if (access_code) {
> > >          r->access_code = 0;
> > >
> > > -        if (access_code == NGX_HTTP_FORBIDDEN) {
> > > +        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> > > +
> > > +        if ((clcf->log_forbidden) && (access_code == NGX_HTTP_FORBIDDEN)) {
> > >              ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
> > >                            "access forbidden by rule");
> > >          }
> >
> > Requests rejected by the access phase handlers are generally
> > expected to be logged.  Also, there are various other cases when
> > access phase handlers log errors, such as in the
> > ngx_http_auth_basic_module on user/password mismatch.
> >
> > While changing this is possible and I can't say I strongly object
> > it, I would like to ensure that this is done in some consistent
> > manner, affecting all the standard access modules, and not just
> > the errors typically returned by the access module.
> >
> > Note well that there are various other places where
> > NGX_HTTP_FORBIDDEN can be returned, and these log errors,
> > including with your patch - autoindex, random index, flv, mp4, and
> > static modules all log errors and return NGX_HTTP_FORBIDDEN when
> > the file/directory cannot be opened due to OS-level access
> > restrictions, and your patch only changes relevant code in the
> > index module.  It is not immediately clear how these are different
> > if the "log_forbidden" directive is expected to behave like
> > "log_not_found".  And, at the same time, it might be seen as a
> > configuration error when a file which cannot be opened by
> > [free]nginx is accessible under the document root, so not logging
> > the error might be the wrong way to go.
> >
> > Also, it might be a better idea to follow the log limiting
> > approach as used by the limit_req and limit_conn modules, that is,
> > introduce some directives similar to limit_req_log_level.
> >
> > Not well that there is a couple of style issues in the code
> > suggested: it should be faster to check "access_code ==
> > NGX_HTTP_FORBIDDEN" first, similarly to how clcf->log_not_found is
> > checked in the static module, and there is no need to place extra
> > parentheses where execution order is well defined and immediately
> > obvious.
> >
> > > @@ -1273,9 +1283,10 @@
> > >  ngx_http_core_content_phase(ngx_http_request_t *r,
> > >      ngx_http_phase_handler_t *ph)
> > >  {
> > > -    size_t     root;
> > > -    ngx_int_t  rc;
> > > -    ngx_str_t  path;
> > > +    size_t                     root;
> > > +    ngx_int_t                  rc;
> > > +    ngx_str_t                  path;
> > > +    ngx_http_core_loc_conf_t  *clcf;
> > >
> > >      if (r->content_handler) {
> > >          r->write_event_handler = ngx_http_request_empty_handler;
> > > @@ -1306,7 +1317,11 @@
> > >
> > >      if (r->uri.data[r->uri.len - 1] == '/') {
> > >
> > > -        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
> > > +        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> > > +
> > > +        if ((clcf->log_forbidden)
> > > +                && (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL)) {
> > > +
> >
> > Style issues here: unneeded parentheses, wrong indentation of the
> > second line, "{" after multiline conditions should be on its own
> > line.
> >
> > >              ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
> > >                            "directory index of \"%s\" is forbidden", path.data);
> > >          }
> > > @@ -3649,6 +3664,7 @@
> > >      clcf->port_in_redirect = NGX_CONF_UNSET;
> > >      clcf->msie_padding = NGX_CONF_UNSET;
> > >      clcf->msie_refresh = NGX_CONF_UNSET;
> > > +    clcf->log_forbidden = NGX_CONF_UNSET;
> > >      clcf->log_not_found = NGX_CONF_UNSET;
> > >      clcf->log_subrequest = NGX_CONF_UNSET;
> > >      clcf->recursive_error_pages = NGX_CONF_UNSET;
> > > @@ -3923,6 +3939,7 @@
> > >      ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);
> > >      ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1);
> > >      ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0);
> > > +    ngx_conf_merge_value(conf->log_forbidden, prev->log_forbidden, 1);
> > >      ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);
> > >      ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0);
> > >      ngx_conf_merge_value(conf->recursive_error_pages,
> >
> > > # diff -Nau freenginx-1.30.0/src/http/modules/ngx_http_index_module.c freenginx-1.30.0_patched/src/http/modules/ngx_http_index_module.c >ngx_http_index_module.c.patch
> > > --- freenginx-1.30.0/src/http/modules/ngx_http_index_module.c 2026-04-14 05:23:27.000000000 -0300
> > > +++ freenginx-1.30.0_patched/src/http/modules/ngx_http_index_module.c 2026-04-28 20:07:00.825547974 -0300
> > > @@ -20,6 +20,7 @@
> > >  typedef struct {
> > >      ngx_array_t             *indices;    /* array of ngx_http_index_t */
> > >      size_t                   max_index_len;
> > > +    ngx_flag_t               off;
> > >  } ngx_http_index_loc_conf_t;
> > >
> > >
> > > @@ -109,6 +110,12 @@
> > >      ngx_http_index_loc_conf_t    *ilcf;
> > >      ngx_http_script_len_code_pt   lcode;
> > >
> > > +    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
> > > +
> > > +    if (ilcf->off) {
> > > +        return NGX_DECLINED;
> > > +    }
> > > +
> > >      if (r->uri.data[r->uri.len - 1] != '/') {
> > >          return NGX_DECLINED;
> > >      }
> >
> > I believe checking r->uri before and method before even trying to
> > access the configuration is optimal from performance point of
> > view, and this is what index module does now, and other modules do
> > as well (see autoindex or random index for some examples).  There
> > is no need to change this order.
> >
> > > @@ -117,7 +124,6 @@
> > >          return NGX_DECLINED;
> > >      }
> > >
> > > -    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
> > >      clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> > >
> > >      allocated = 0;
> > > @@ -369,8 +375,10 @@
> > >      u_char *file, ngx_err_t err)
> > >  {
> > >      if (err == NGX_EACCES) {
> > > -        ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
> > > -                      "\"%s\" is forbidden", file);
> > > +        if (clcf->log_forbidden) {
> > > +            ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
> > > +                          "\"%s\" is forbidden", file);
> > > +        }
> > >
> > >          return NGX_HTTP_FORBIDDEN;
> > >      }
> >
> > Note that this change should be in a separate patch, and see above
> > for more comments about this.
> >
> > > @@ -396,6 +404,7 @@
> > >
> > >      conf->indices = NULL;
> > >      conf->max_index_len = 0;
> > > +    conf->off = NGX_CONF_UNSET;
> > >
> > >      return conf;
> > >  }
> > > @@ -409,6 +418,12 @@
> > >
> > >      ngx_http_index_t  *index;
> > >
> > > +    ngx_conf_merge_value(conf->off, prev->off, 0);
> > > +
> > > +    if (conf->off) {
> > > +        return NGX_CONF_OK;
> > > +    }
> > > +
> > >      if (conf->indices == NULL) {
> > >          conf->indices = prev->indices;
> > >          conf->max_index_len = prev->max_index_len;
> >
> > Consider the following configuration:
> >
> >     server {
> >         index off;
> >
> >         location /public/ {
> >             index index.html;
> >         }
> >     }
> >
> > With the merge logic you've implemented this will result in
> > "conf->off" being set in "location /public/", leading to no index
> > files being used there, which is obviously not the requested
> > behaviour.
> >
> > There are multiple possible solutions here:
> >
> > - write a custom merge logic, checking both conf->off and
> >   conf->indices and inheriting conf->off and conf->indices only if
> >   neither of the two was set at the current level,
> >
> > - change the code to instead rely on conf->indices special values
> >   (for example, conf->indices == NGX_CONF_UNSET_PTR for not set,
> >   and NULL for off; or indices->nelts == 0 for off),
> >
> > - change "off" to an additional flag, similarly to how it is done
> >   with "max_index_len", and inherit it along with conf->indices.
> >
> > > @@ -470,6 +485,22 @@
> > >      ngx_http_index_t           *index;
> > >      ngx_http_script_compile_t   sc;
> > >
> > > +    value = cf->args->elts;
> > > +
> > > +    if (ngx_strcmp(value[1].data, "off") == 0) {
> > > +        if (cf->args->nelts == 2) {
> > > +
> > > +            ilcf->off = 1;
> > > +
> > > +            return NGX_CONF_OK;
> > > +        } else {
> > > +
> > > +          ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> > > +                             "\"index off\" must be the only parameter");
> > > +          return NGX_CONF_ERROR;
> > > +        }
> > > +    }
> > > +
> >
> > Consider the following configuration:
> >
> >     index index.html;
> >     index off;
> >
> > It is expected to be equivalent to "index index.html off;" (if
> > supported; currently it is), yet these behave very differently
> > with your code: multiple directives will switch off indexing and
> > will silently ignore "index.html", yet "index index.html off;"
> > will try to access the "off" index file.
> >
> > Similarly,
> >
> >     index default.html;
> >     index off index.html;
> >
> > is not equivalent to "index default.html off index.html;" and will
> > be rejected by your code.
> >
> > (Also, there are multiple style issues here: wrong indentation,
> > inconsistent empty lines.)
> >
> > >      if (ilcf->indices == NULL) {
> > >          ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
> > >          if (ilcf->indices == NULL) {
> > > @@ -477,8 +508,6 @@
> > >          }
> > >      }
> > >
> > > -    value = cf->args->elts;
> > > -
> > >      for (i = 1; i < cf->args->nelts; i++) {
> > >
> > >          if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
> >
> > > # diff -Nau freenginx-1.30.0/src/http/ngx_http_core_module.h freenginx-1.30.0_patched/src/http/ngx_http_core_module.h >ngx_http_core_module.h.patch
> > > --- freenginx-1.30.0/src/http/ngx_http_core_module.h  2026-04-14 05:23:27.000000000 -0300
> > > +++ freenginx-1.30.0_patched/src/http/ngx_http_core_module.h  2026-04-28 19:56:25.382446864 -0300
> > > @@ -406,6 +406,7 @@
> > >      ngx_flag_t    port_in_redirect;        /* port_in_redirect */
> > >      ngx_flag_t    msie_padding;            /* msie_padding */
> > >      ngx_flag_t    msie_refresh;            /* msie_refresh */
> > > +    ngx_flag_t    log_forbidden;           /* log_forbidden */
> > >      ngx_flag_t    log_not_found;           /* log_not_found */
> > >      ngx_flag_t    log_subrequest;          /* log_subrequest */
> > >      ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
> >
> > > # diff -Nau freenginx-1.30.0/src/http/modules/ngx_http_access_module.c freenginx-1.30.0_patched/src/http/modules/ngx_http_access_module.c >ngx_http_access_module.c.patch
> > > --- freenginx-1.30.0/src/http/modules/ngx_http_access_module.c        2026-04-14 05:23:27.000000000 -0300
> > > +++ freenginx-1.30.0_patched/src/http/modules/ngx_http_access_module.c        2026-04-28 19:56:52.838228281 -0300
> > > @@ -280,7 +280,7 @@
> > >      if (deny) {
> > >          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
> > >
> > > -        if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
> > > +        if ((clcf->log_forbidden) && (clcf->satisfy == NGX_HTTP_SATISFY_ALL)) {
> > >              ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
> > >                            "access forbidden by rule");
> > >          }
> >
> > Hope this helps.
> 
> Thank you for your time and help!!
> 
> As you mentioned, the "log_forbidden" change is completely orthogonal,
> so let's set it aside for now.

Good.

> While searching for a code pattern in the freenginx codebase to guide
> my implementation, I accidentally discovered a bug in
> ngx_http_log_module (ngx_http_log_open_file_cache) when testing the
> "off" parameter in different positions. For example, the configuration
> "open_log_file_cache off max=1;" should fail, but it does not.

Most of the named argument directive parsers are somewhat sloppy - 
in particular, they fail to detect duplicate arguments (such as in 
"open_file_cache max=1 max=2;") and often fail to detect 
conflicting arguments, as in your example.

> I hope this patch works well! Thank you, again!
> Fabiano Furtado

> diff -r f347a195b373 src/http/modules/ngx_http_index_module.c
> --- a/src/http/modules/ngx_http_index_module.c	Thu Apr 30 07:25:52 2026 +0300
> +++ b/src/http/modules/ngx_http_index_module.c	Sun May 03 22:15:23 2026 -0300
> @@ -20,6 +20,7 @@
>  typedef struct {
>      ngx_array_t             *indices;    /* array of ngx_http_index_t */
>      size_t                   max_index_len;
> +    ngx_flag_t               off;

The "ngx_flag_t" type is used for configuration directives with 
the ngx_conf_set_flag_slot() handler.  There is no need to use 
this type for fields which aren't with ngx_conf_set_flag_slot().

In this particular case, just one bit should be enough.  But since 
there are no other bit fields in the structure, there is no need 
to declare it as a bit field, as there will be no memory benefits, 
and might be performance degradation.  Existing approach for such 
cases is to use the ngx_uint_t type and provide corresponding bit 
field type in a comment, e.g.:

    ngx_uint_t               off;        /* unsigned:1 */

You can see an example in the access log module you've mentioned.

>  } ngx_http_index_loc_conf_t;
>  
>  
> @@ -109,6 +110,12 @@
>      ngx_http_index_loc_conf_t    *ilcf;
>      ngx_http_script_len_code_pt   lcode;
>  
> +    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
> +
> +    if (ilcf->off) {
> +        return NGX_DECLINED;
> +    }
> +
>      if (r->uri.data[r->uri.len - 1] != '/') {
>          return NGX_DECLINED;
>      }

You've probably missed the comment about this code in the previous 
review:

> > I believe checking r->uri before and method before even trying to
> > access the configuration is optimal from performance point of
> > view, and this is what index module does now, and other modules do
> > as well (see autoindex or random index for some examples).  There
> > is no need to change this order.

> @@ -117,7 +124,6 @@
>          return NGX_DECLINED;
>      }
>  
> -    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
>      clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
>  
>      allocated = 0;
> @@ -396,6 +402,7 @@
>  
>      conf->indices = NULL;
>      conf->max_index_len = 0;
> +    conf->off = 0;
>  
>      return conf;
>  }
> @@ -409,31 +416,34 @@
>  
>      ngx_http_index_t  *index;
>  
> -    if (conf->indices == NULL) {
> -        conf->indices = prev->indices;
> -        conf->max_index_len = prev->max_index_len;
> +    if (conf->indices || conf->off) {
> +        return NGX_CONF_OK;
> +    }
> +
> +    conf->indices = prev->indices;
> +    conf->max_index_len = prev->max_index_len;
> +    conf->off = prev->off;
> + 
> +    if (conf->indices || conf->off) {
> +        return NGX_CONF_OK;
>      }

In general, an early return from a merge function is better 
avoided: it makes extending the function non-trivial, so 
introducing additional configuration directives will be 
complicated.

>  
> +    conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
>      if (conf->indices == NULL) {
> -        conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
> -        if (conf->indices == NULL) {
> -            return NGX_CONF_ERROR;
> -        }
> +        return NGX_CONF_ERROR;
> +    }
>  
> -        index = ngx_array_push(conf->indices);
> -        if (index == NULL) {
> -            return NGX_CONF_ERROR;
> -        }
> +    index = ngx_array_push(conf->indices);
> +    if (index == NULL) {
> +        return NGX_CONF_ERROR;
> +    }
>  
> -        index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
> -        index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
> -        index->lengths = NULL;
> -        index->values = NULL;
> +    index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
> +    index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
> +    index->lengths = NULL;
> +    index->values = NULL;
>  
> -        conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
> -
> -        return NGX_CONF_OK;
> -    }
> +    conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
>  
>      return NGX_CONF_OK;
>  }
> @@ -470,6 +480,28 @@
>      ngx_http_index_t           *index;
>      ngx_http_script_compile_t   sc;
>  
> +    value = cf->args->elts;
> +
> +    if (ngx_strcmp(value[1].data, "off") == 0) {
> +        if (cf->args->nelts == 2) {
> +            ilcf->off = 1;
> +            return NGX_CONF_OK;
> +        }
> + 
> +        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> +                           "invalid parameter \"%V\"", &value[2]);
> +        return NGX_CONF_ERROR;
> +    }
> +
> +    for (i = 2; i < cf->args->nelts; i++) {
> +
> +        if (ngx_strcmp(value[i].data, "off") == 0) {
> +            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
> +                               "parameter \"off\" must be declared first");
> +            return NGX_CONF_ERROR;
> +        }
> +    }

I believe there is no need for an additional loop here, we already 
have one (and it already does a couple of similar checks).

> +
>      if (ilcf->indices == NULL) {
>          ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
>          if (ilcf->indices == NULL) {
> @@ -477,8 +509,6 @@
>          }
>      }
>  
> -    value = cf->args->elts;
> -
>      for (i = 1; i < cf->args->nelts; i++) {
>  
>          if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {

Overall, looking through a couple of variants I tend to think that 
using licf->indices == NULL as a special value would be optimal 
here.  Suggested patch below.

# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1777971559 -10800
#      Tue May 05 11:59:19 2026 +0300
# Node ID e8bd179e5e7be4e6422918dbc6cace143de7b8ce
# Parent  f347a195b373c1d118fd8a929886692f052e8d4b
Index: added "index off;".

This might be useful to save a syscall if index files should not be used,
for example, when autoindex is expected to be used instead.

Prodded by Fabiano Furtado.

diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -120,6 +120,10 @@ ngx_http_index_handler(ngx_http_request_
     ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
+    if (ilcf->indices == NULL) {
+        return NGX_DECLINED;
+    }
+
     allocated = 0;
     root = 0;
     dir_tested = 0;
@@ -394,7 +398,7 @@ ngx_http_index_create_loc_conf(ngx_conf_
         return NULL;
     }
 
-    conf->indices = NULL;
+    conf->indices = NGX_CONF_UNSET_PTR;
     conf->max_index_len = 0;
 
     return conf;
@@ -409,12 +413,12 @@ ngx_http_index_merge_loc_conf(ngx_conf_t
 
     ngx_http_index_t  *index;
 
-    if (conf->indices == NULL) {
+    if (conf->indices == NGX_CONF_UNSET_PTR) {
         conf->indices = prev->indices;
         conf->max_index_len = prev->max_index_len;
     }
 
-    if (conf->indices == NULL) {
+    if (conf->indices == NGX_CONF_UNSET_PTR) {
         conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
         if (conf->indices == NULL) {
             return NGX_CONF_ERROR;
@@ -470,15 +474,30 @@ ngx_http_index_set_index(ngx_conf_t *cf,
     ngx_http_index_t           *index;
     ngx_http_script_compile_t   sc;
 
+    value = cf->args->elts;
+
+    if (cf->args->nelts == 2
+        && ngx_strcmp(value[1].data, "off") == 0)
+    {
+        if (ilcf->indices != NGX_CONF_UNSET_PTR) {
+            return "is duplicate";
+        }
+
+        ilcf->indices = NULL;
+        return NGX_CONF_OK;
+    }
+
     if (ilcf->indices == NULL) {
+        return "is duplicate";
+    }
+
+    if (ilcf->indices == NGX_CONF_UNSET_PTR) {
         ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
         if (ilcf->indices == NULL) {
             return NGX_CONF_ERROR;
         }
     }
 
-    value = cf->args->elts;
-
     for (i = 1; i < cf->args->nelts; i++) {
 
         if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
@@ -494,6 +513,13 @@ ngx_http_index_set_index(ngx_conf_t *cf,
             return NGX_CONF_ERROR;
         }
 
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "index \"%V\" in \"index\" directive is invalid",
+                               &value[i]);
+            return NGX_CONF_ERROR;
+        }
+
         index = ngx_array_push(ilcf->indices);
         if (index == NULL) {
             return NGX_CONF_ERROR;
# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1777971696 -10800
#      Tue May 05 12:01:36 2026 +0300
# Node ID 6feef2c09b82aede49cb96ae8ddcf1dce37c5c67
# Parent  e8bd179e5e7be4e6422918dbc6cace143de7b8ce
Index: fixed error message about empty index.

diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -509,7 +509,7 @@ ngx_http_index_set_index(ngx_conf_t *cf,
         if (value[i].len == 0) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "index \"%V\" in \"index\" directive is invalid",
-                               &value[1]);
+                               &value[i]);
             return NGX_CONF_ERROR;
         }
 


-- 
Maxim Dounin
http://mdounin.ru/


More information about the nginx-devel mailing list