如果文件夹不存在,Nginx 显示自定义错误页面

Nginx display custom error page if folder does not exist

如果缺少文件夹,我想显示自定义错误页面。

我的目录结构如下:

data
   defaults
      error.html
      nothosted.html
   example.com
      misc
         error.html
   example.net
   ...

因此,当有人访问 example.de 时,其文件夹不存在 defaults/nothosted.html 会显示。但是,如果该文件夹存在,则应使用我的@error 位置。

目前我的配置不起作用。

    server_name ~^(\*\.)?(?<subdomain>[a-z\d][a-z\d-]*[a-z\d]\.)(?<domain>.+)+$;

    location / {
        root /data/$subdomain$domain;
        try_files $uri $uri/ @nothosted;
        index index.html index.htm index.php;
    }

    location @nothosted {
        root      /data;

        set $link /$subdomain$domain;

        if (!-d $link) {
           set $link /defaults/nothosted.html;
        }

        try_files  $link @error;
    }

    error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error;
    location @error {
        internal;
        ssi         on;
        auth_basic  off;
        root        /data;
        try_files   /$subdomain$domain/misc/error.html /$domain/misc/error.html /defaults/error.html =404;
    }

我在 nginx 日志中得到的错误如下:

*1 directory index of "/data/" is forbidden

我在尝试连接到其文件夹不存在的域时遇到的错误是来自 @error 位置的“403 forbidden”。

权限应该是正确的(整个/data/文件夹归nginx用户所有)

编辑: 使用以下非托管位置时,访问后网页上会显示正确的 link。所以问题必须是 try_files 函数。

    location @nothosted {
        root      /data;

        set $link /$subdomain$domain;

        if (!-d $document_root/$subdomain$domain) {
           set $link /defaults/nothosted.html;
        }

        return 200 $link;
        add_header Content-Type text/plain;
    }

无法按照您尝试解决的方式解决此问题。原因是 if is evillocation 上下文中使用。早就知道了,没想到有时候会这么邪恶

通常 nginx 配置指令是声明性的(例如,您将像 proxy_pass 这样的指令放在哪里没有区别,它可以放在任何地方的确切位置)。虽然通常来自重写模块的 nginx 指令是唯一可以被视为命令式的指令(请参阅类似于 [=19= 的 internal implementation chapter from the module documentation), an if directive is a very special case. After reading the aforementioned implementation description, for a long time I thought that using only directives from the rewrite module inside the if block will made such a block completely safe one. Unfortunately it isn't true. One of the best descriptions of how this directive actually works was made by Yichun Zhang, an author of famous lua-nginx-module and the OpenResty bundle. If fact every if directive implicitly creates a nested location which tries to inherit all the declarations from the parent one. However the try_files directive is not gets inherited (nginx trac ticket), and such a virtual nested location, if being selected to handle the request and having a static content handler, would have a default PRECONTENT phase 处理程序)。您收到的错误消息来自隐式 try_files 指令尝试使用当前 URI(可能是 / 一个)并且隐式 index 指令无法在 webroot /data 目录中找到索引文件。

也就是说,选择的方法是行不通的。您可以尝试以下方法:

    if (!-d /data/$subdomain$domain) {
        rewrite ^ /defaults/nothosted.html;
    }

    location / {
        root /data/$subdomain$domain;
        try_files $uri $uri/ =404;
        index index.html index.htm index.php;
    }

    location = /defaults/nothosted.html {
        internal;
        root      /data;
    }

    error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error;
    location @error {
        internal;
        ssi         on;
        auth_basic  off;
        root        /data;
        try_files   /$subdomain$domain/misc/error.html /$domain/misc/error.html /defaults/error.html =404;
    }

此解决方案有一个缺点 - /defaults/nothosted.html URI 不适用于任何托管站点。如果您觉得不可接受,可以使用某种独特的随机字符串代替 nothosted(并相应地重命名该 nothosted.html 文件)。