¿Cómo configurar una página de error de reserva en nginx?

10

Estoy configurando el manejo de nginx de algunas páginas de error y otros archivos multimedia "predeterminados" (como favicon.ico y robots.txt) en este momento y me encontré con un pequeño problema para que las cosas funcionen como quiero para ciertas páginas de error .

Básicamente, lo que intento hacer es servir ciertos archivos para un servidor bajo la raíz de ese servidor, por ejemplo /var/www/someserver.com/robots.txt. Si ese archivo no existe, quiero que nginx vaya al "valor predeterminado", es decir /var/www/default/robots.txt. Esta es la esencia básica de cómo tengo eso (con éxito) configurado:

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

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

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

Eso funciona muy bien.

Estoy tratando de hacer lo mismo para las páginas de error, y no puedo hacer que eso suceda:

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

    error_page 404   /404.html;

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

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

Tenga en cuenta que esto "funciona" en el sentido de que si visita someserver.com/404.html, primero intentará cargar /var/www/someserver.com/404.html y luego volver a / var / www / default /404.html si eso no se encuentra. Sin embargo, si visita someserver.com/blahblah, solo muestra la página 404 si está configurada en /var/www/someserver.com/. No vuelve al directorio predeterminado si ese archivo no existe.

De todos modos, probablemente pueda lo que estaba tratando de lograr (es por eso que incluí el primer ejemplo de trabajo).

¿Algunas ideas?

Editar:

Según la respuesta de Martin F, esto es lo que terminé armando:

# 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;
    }
}

Esto funciona genial. El bloque real de páginas y ubicaciones de error anteriores se encuentra en un archivo server_defaults.conf que se incluye con cada host virtual, por lo que no codifiqué la ruta en cada ubicación y usé una ruta relativa para los valores predeterminados.

Edición 2:

Este enfoque tiene un problema. Si POST a una URL que devuelve un error, el método de solicitud POST se envía con los intentos try_files. Esto (para mí) da como resultado un error 405 No permitido, porque nginx esencialmente está intentando PUBLICAR, por ejemplo, /default/500.html en lugar de solo obtener esa página.

Edición 3:

Publiqué una solución que funciona que está mucho más cerca de mi idea original.

Jim D
fuente

Respuestas:

10

Terminé yendo con algo mucho más cercano a mi idea original. La clave que me faltaba resultó ser la directiva recursive_error_pages. Todo lo que realmente tenía que hacer era encenderlo y mi idea original funcionó. Así es como se ve la parte relevante de mi conf ahora:

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í otros tipos de error que no eran parte de mi pregunta original aquí porque esto es lo que terminó causándome dificultades con el enfoque de Martin F, que de otra manera fue excelente. La log_not_founddirectiva solo garantiza que no obtenga 404 en mi registro cuando la página de error no se encuentra en la raíz original.

Jim D
fuente
Esto funciona mejor (especialmente la combinación 404 / POST), pero parece que se traga el código de error HTTP y envía 200 ... ¿Tiene el mismo comportamiento que yo?
Oct
2
Es posible que desee eliminar log_not_found off;y agregar en su internal;lugar.
Alix Axel
El problema que notó con una solicitud POST que resultó en un error es el comportamiento extraño de nginx, consulte trac.nginx.org/nginx/ticket/824
Robo el
5

try_files es el camino a seguir aquí. La siguiente configuración debería funcionar, pero no la he probado para detectar errores de sintaxis.

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
fuente
Sí, eso es exactamente lo que necesitaba. Intenté usar try_files pero su respuesta me llevó a darme cuenta de que necesitaba crear una ubicación interna especial para ello. Edité mi respuesta para mostrar la configuración que funcionó para mí, que tiene algunos ajustes a lo que sugirió.
Jim D
Vea mi edición 2 arriba. Por lo que puedo decir, esta sugerencia no funciona con solicitudes POST. Dado que try_files utiliza el método de solicitud de la solicitud original, terminará con una respuesta 405 No permitida.
Jim D
@JimD: Bueno, no usas exactamente este enfoque, utilizas uno modificado. Esto funciona para mi. Intente actualizar su binario Nginx si es anterior a 0.8.x
Martin Fjordvald
Bueno, el problema es que estoy usando esto para más que solo 404s, por lo que tengo que hacer error_page -> location -> try_files en lugar de location -> try_files -> location -> try_files tal como estás. Sin embargo, estoy en un binario 0.7.x de un paquete, así que intentaré compilarlo desde la fuente y ver si eso lo resuelve.
Jim D
2

Desafortunadamente, llego un par de años tarde con mi respuesta, pero pensé que podría ayudar a los futuros buscadores. Mi versión de Nginx instalada es 1.2.4, y he creado el siguiente fragmento de configuración,

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
fuente
1
Buen uso de internal.
Clint Pachl