11

I'm configuring nginx's handling of some error pages and other "default" media files (such as favicon.ico and robots.txt) at the moment and I ran into a minor problem getting things to work the way I want for certain error pages.

Basically, what I'm trying to do is serve certain files for a server under the root for that server, e.g. /var/www/someserver.com/robots.txt. If that file does not exist, I want nginx to go to the "default", i.e. /var/www/default/robots.txt. This is the basic gist of how I have that (successfully) configured:

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

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

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

That works great.

I'm trying to do the same for error pages, and I'm not able to make that happen though:

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

    error_page 404   /404.html;

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

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

Note that this "works" in the sense that if you visit someserver.com/404.html, it will first try to load /var/www/someserver.com/404.html and then fall back to /var/www/default/404.html if that's not found. However, if you visit someserver.com/blahblah, it only shows the 404 page if it's set in /var/www/someserver.com/. It does not fall back to the default directory if that file doesn't exist.

Anyhow, you can probably what I was trying to accomplish (that's why I included the first working example).

Any ideas?

Edit:

Based on Martin F's answer, this is what I ended up putting together:

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

This works awesome. The actual block of error_pages and locations above is in a server_defaults.conf file that gets included with every virtual host, which is why I didn't hard code the path into each location and used a relative path for the defaults.

Edit 2:

This approach has a problem. If you POST to a URL that returns an error, the POST request method gets sent with the try_files attempts. This (for me) results in a 405 Not Allowed error, because nginx is essentially trying to POST to e.g. /default/500.html instead of just getting that page.

Edit 3:

I posted a solution that works that's a lot closer to my original idea.

4 Answers 4

14

I ended up going with something a lot closer to my original idea. The key I was missing turned out to be the directive recursive_error_pages. All I really had to do was turn this "on" and my original idea worked. Here's what the relevant part of my conf looks like now:

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

I included other error types that were not part of my original question here because this is what ended up causing me difficulties with Martin F's approach, which was otherwise excellent. The log_not_found directive just ensures that I don't get 404s in my log when the error page isn't found in the original root.

3
  • This works greate (esp. re. the 404 / POST combination ) but it seems to swallow the HTTP Error code and sends 200... Do you have the same behaviour as I do ?
    – Oct
    Oct 28, 2011 at 14:38
  • 2
    You may want to remove log_not_found off; and add internal; instead.
    – Alix Axel
    Dec 13, 2012 at 2:36
  • The issue you noticed with a POST request resulting in an error is weird behaviour by nginx, see trac.nginx.org/nginx/ticket/824
    – Robo
    Oct 6, 2016 at 4:02
5

try_files is the way to go here. The following configuration should work, but I haven't tested it for syntax errors.

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.
    }
}
4
  • Yes, that's exactly what I needed. I had tried using try_files but your answer led me to realize that I needed to create a special internal location for it. I've edited my answer to show the configuration that worked for me, which has a few tweaks to what you suggested.
    – Jim D
    Mar 25, 2011 at 12:32
  • See my edit 2 above. From what I can tell, this suggestion does not work with POST requests. Since try_files uses the request method of the original request, you end up with a 405 Not Allowed response.
    – Jim D
    Mar 26, 2011 at 14:17
  • @JimD: Well you don't exactly use this approach, you use a modified one. This works for me. Try upgrading your Nginx binary if it's older than 0.8.x Mar 26, 2011 at 16:23
  • Well, the problem is I'm using this for more than just 404s, which is why I have to do error_page -> location -> try_files instead of location -> try_files -> location -> try_files as you are. I am however on an 0.7.x binary from a package, so I'll try building it from source and seeing if that solves it.
    – Jim D
    Mar 26, 2011 at 16:31
3

Unfortunately I'm a couple years late with my answer, but I thought it might help the future searchers. My installed Nginx version is 1.2.4, and I've created the following configuration snippet,

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

}
1
  • 1
    Good use of internal. May 23, 2014 at 11:44
0

I found it was crucial to include "proxy_intercept_errors on;" in any location block where error_page was also included.

Without this setting, then the location block will intercept error codes coming back from the upstream.

http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .