Como configurar uma página de erro de fallback no nginx?

10

Estou configurando o nginx para lidar com algumas páginas de erro e outros arquivos de mídia "padrão" (como favicon.ico e robots.txt) no momento, e tive um pequeno problema para que as coisas funcionassem da maneira que desejo para determinadas páginas de erro .

Basicamente, o que estou tentando fazer é fornecer determinados arquivos para um servidor na raiz desse servidor, por exemplo, /var/www/someserver.com/robots.txt. Se esse arquivo não existir, quero que o nginx vá para o "padrão", ou seja, /var/www/default/robots.txt. Esta é a essência básica de como eu tenho isso (com sucesso) configurado:

server {
    ...
    root /var/www/someserver.com;

    location ~* ^/(robots\.txt)$ {
        error_page 404 = @default;
    }

    location @default {
        root /var/www/default;
    }
}

Isso funciona muito bem.

Estou tentando fazer o mesmo com páginas de erro e, no entanto, não consigo fazer isso acontecer:

server {
    ...
    root /var/www/someserver.com;

    error_page 404   /404.html;

    location ~* ^/(404\.html)$ {
        error_page 404 = @default;
    }

    location @default {
        root /var/www/default;
    }
}

Observe que isso "funciona" no sentido de que se você visitar someserver.com/404.html, primeiro tentará carregar /var/www/someserver.com/404.html e depois voltará para / var / www / default /404.html se não for encontrado. No entanto, se você visitar someserver.com/blahblah, ele exibirá a página 404 apenas se estiver definida em /var/www/someserver.com/. Ele não retorna ao diretório padrão se esse arquivo não existir.

De qualquer forma, você pode provavelmente o que eu estava tentando realizar (foi por isso que incluí o primeiro exemplo de trabalho).

Alguma ideia?

Editar:

Com base na resposta de Martin F, foi o que acabei montando:

# Doesn't work when error page is returned on a POST request
server {
    ...
    root /var/www/someserver.com;

    error_page  404         = @notfound;
    error_page  500 502 504 = @server_error;
    error_page  503         = @maintenance;

    location @notfound {
        try_files /404.html /../default/404.html =404;
    }

    location @server_error {
        try_files /500.html /../default/500.html =500;
    }

    location @maintenance {
        try_files /503.html /../default/503.html =503;
    }
}

Isso funciona demais. O bloco real de páginas de erro e locais acima está em um arquivo server_defaults.conf que é incluído em todos os hosts virtuais, por isso não codifiquei o caminho em cada local e usei um caminho relativo para os padrões.

Edição 2:

Essa abordagem tem um problema. Se você POSTAR em um URL que retorne um erro, o método de solicitação POST será enviado com as tentativas try_files. Isso (para mim) resulta em um erro 405 não permitido, porque o nginx está essencialmente tentando POST para, por exemplo, /default/500.html, em vez de apenas obter a página.

Edição 3:

Postei uma solução que funciona muito mais próxima da minha ideia original.

Jim D
fonte

Respostas:

10

Acabei indo com algo muito mais próximo da minha ideia original. A chave que faltava acabou sendo a diretiva recursive_error_pages. Tudo o que eu realmente precisava fazer era ativar isso e minha ideia original funcionou. Aqui está a aparência da parte relevante do meu conf:

server {
    ...

    root /var/www/someserver.com/;

    error_page 400 404      /404.html;
    error_page 500 502 504  /500.html;
    error_page 503          /503.html;

    recursive_error_pages   on;

    location ~* ^/(404\.html|500\.html|503\.html)$ {
        log_not_found off;
        error_page 404 = @default;
    }

    location @default {
        log_not_found on;
        root /var/www/default;
    }
}

Incluí outros tipos de erro que não faziam parte da minha pergunta original aqui, porque foi isso que me causou dificuldades na abordagem de Martin F, que, de outra forma, era excelente. A log_not_founddiretiva apenas garante que eu não receba 404s no meu log quando a página de erro não for encontrada na raiz original.

Jim D
fonte
Isso funciona greate (especialmente a combinação 404 / POST), mas parece engolir o código de erro HTTP e envia 200 ... Você tem o mesmo comportamento que eu?
Oct
2
Você pode remover log_not_found off;e adicionar internal;.
Alix Axel
O problema que você notou com uma solicitação POST, resultando em erro, é um comportamento estranho do nginx, consulte trac.nginx.org/nginx/ticket/824
Robo
5

try_files é o caminho a percorrer aqui. A seguinte configuração deve funcionar, mas não a testei para erros de sintaxe.

server {
    ...
    root /var/www;

    location / {
        try_files /someserver.com$uri /default$uri /someserver.com$uri/ /default$uri/ @notfound;
    }

    location @notfound {
       try_files /someserver/404.html /default/404.html =404; # =404 should make it use the nginx-default 404 page.
    }
}
Martin Fjordvald
fonte
Sim, é exatamente disso que eu precisava. Eu tentei usar try_files, mas sua resposta me levou a perceber que eu precisava criar um local interno especial para ele. Editei minha resposta para mostrar a configuração que funcionou para mim, que tem alguns ajustes no que você sugeriu.
Jim D
Veja minha edição 2 acima. Pelo que sei, essa sugestão não funciona com solicitações POST. Como try_files usa o método de solicitação original, você obtém uma resposta 405 não permitida.
Jim D
@ JimD: Bem, você não usa exatamente essa abordagem, usa uma modificada. Isso funciona para mim. Tente atualizar seu binário Nginx se é mais velho do que 0.8.x
Martin Fjordvald
Bem, o problema é que estou usando isso por mais do que apenas 404s, e é por isso que tenho que fazer error_page -> location -> try_files em vez de location -> try_files -> location -> try_files como você é. No entanto, estou em um binário 0.7.x de um pacote, então vou tentar construí-lo a partir do código-fonte e ver se isso o resolve.
Jim D
2

Infelizmente, estou com alguns anos de atraso com a minha resposta, mas achei que poderia ajudar os futuros pesquisadores. Minha versão do Nginx instalada é a 1.2.4 e eu criei o seguinte snippet de configuração,

server {

server_name www.example.com
root /var/www/example


## Errors -> Locations
error_page   400 /400.html;
error_page   403 /403.html;
error_page   404 /404.html;
error_page   500 502 503 504 /50x.html;

## Locations -> Fallback
location = /400.html {
    try_files /400.html @error;
    internal;
}
location = /403.html {
    try_files /403.html @error;
    internal;
}
location = /404.html {
    try_files /404.html @error;
    internal;
}
location = /50x.html {
    try_files /50x.html @error;
    internal;
}

## Fallback Directory
location @error {
    root /var/www/error;
}

}
JM Becker
fonte
1
Bom uso de internal.
Clint Pachl