Nginx custom 404 error page for virtual host

custom-errorsnginxvirtualhost

I'm running a few virtual hosts off nginx, and I would like them to each have their own custom 404 pages. Each virtual host has their own nginx.conf that I include my global nginx.conf.

Inside the config file for the particular virtual host that I can't currently get to work right, the configuration block looks like this:

server{
    listen 80;
    server_name www.foo.com;
    expires     1M;
    access_log  /srv/www/foo/logs/acces.log;
    error_log   /srv/www/foo/logs/error.log;

    error_page 404 /404.html;

    location / {
        root    /srv/www/foo/public_html;
        index   index.html;
        rewrite ^/(.*)/$ /$1 permanent;
        try_files "${uri}.html" $uri $uri/ =404;
    }

    location = /404.html {
        internal;
    }       
}

404'ing itself is fine, but when a 404 status code is returned, the default nginx 404 page is displayed and not the custom page, 404.html, that lives in the site root.

I've tried absolute pathing via both the file system and the site's URI to the error page, and I've tried putting the error_page directive in the location block. Neither of those worked. My suspicion is that the problem is coming from my rewrite and try_files combo that I'm using to "prettify" my URLs.

Best Answer

Firstly, set your root directive outside the location block - in this case, above the error_page - directive. See the nginx pitfalls page. The reason for this is that many directives (including error_page) need to know paths relative to something - and all paths are assumed to be relative (i.e. /404.html is not in the root of your file system - it is relative to your document root).

server{
    #listen 80; - not needed
    server_name www.foo.com;
    root    /srv/www/foo/public_html;
    expires     1M;
    access_log  /srv/www/foo/logs/access.log;
    error_log   /srv/www/foo/logs/error.log;

    error_page 404 /404.html;

    location / {
        index   index.html;
        rewrite ^/(.*)/$ /$1 permanent;
        try_files "${uri}.html" $uri $uri/ =404;
    }

    location = /404.html {
        internal;
    }       
}

If the above does not resolve your error, increase the verbosity of your error_log (e.g. to 'info') and see what page nginx is actually serving when you hit a 404.