3

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.

Doug Stephen
  • 305
  • 1
  • 3
  • 8
  • 1
    Firstly, set your root directive outside the location block - in this case, above the error_page directive. Secondly, increase the verbosity of your error_log (e.g. to 'info') and see what page nginx is actually serving when you hit a 404 (update your question with relevant sections of your logs if needed). – cyberx86 Jan 29 '12 at 20:04
  • Moving my root directory directive to server scope fixed the problem. If you add this as an answer I can upvote it and accept it for you. – Doug Stephen Jan 29 '12 at 23:16
  • The positioning of the root directive definitely seems to be one of the most common causes of problems in nginx setups. You should be able to move the index directive out of your location block as well. As an aside, the combination of the rewrite and try_files seems a bit unusual - I might even suggest a separate location block for the directories matching your rewrite, and a simplified try_files. – cyberx86 Jan 30 '12 at 00:32
  • I'm using the combo of try_files and rewrites to remove file extensions from all url's and to rewrite all url's with a trailing backslash to a slash less format to maintain compatibility with old links and bookmarks from a legacy URL scheme. This just seemed the quickest way to do so. – Doug Stephen Jan 30 '12 at 01:35

1 Answers1

5

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.

Michael Hampton
  • 237,123
  • 42
  • 477
  • 940
cyberx86
  • 20,620
  • 1
  • 60
  • 80