33

I have nginx 0.8.53 configured with some virtual hosts which work as desired. However, due to nginx's "best match" on virtual hosts, I need to add a default host to catch all requests that aren't for a specific virtual host. I would like the default host to return a custom 404 page that I created instead of the default nginx 404 page.

I assumed I needed something like:

# The default server:
server {
    listen       80 default_server;
    server_name  everythingelse;

    # Everything is a 404
    location / {
        return 404;
    }
    error_page 404 /opt/local/html/404.html;
}

But this still returns the default nginx 404 page. It seems the return 404 ignores the error_page config.

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Tim P
  • 948
  • 1
  • 12
  • 19

6 Answers6

49

Here what I have in my conf to make it work:

# The default server.
server {
  listen       80 default_server;
  server_name  everythingelse;

  error_page 404 /404.html;

  # Everything is a 404
  location / {
    return 404; #return the code 404
  }

  # link the code to the file
  location = /404.html {
    #EDIT this line to make it match the folder where there is your errors page
    #Dont forget to create 404.html in this folder
    root  /var/www/nginx/errors/;
  }
}
Luke Peterson
  • 8,584
  • 8
  • 45
  • 46
0x1gene
  • 3,349
  • 4
  • 29
  • 48
  • Thanks, this works for me. Btw, you can ignore the last `/` in `root /var/www/nginx/errors/;`. Write it as `root /var/www/nginx/errors;` – AurevoirXavier May 07 '18 at 09:42
10

Very few directives in nginx take a filesystem path. You want something like:

# The default server.
server {
  listen       80 default_server;
  server_name  everythingelse;

  root /opt/local/html;

  error_page 404 /404.html;

  # Everything is a 404
  location / {
    return 404;
  }

  # EDIT: You may need this to prevent return 404; recursion
  location = /404.html {
    internal;
  }
}
Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
kolbyjack
  • 17,660
  • 5
  • 48
  • 35
  • Your code will give him the default 404 page which he says he doesn't want (and already gets anyway) and secondly, the error_page directive allows you to specify a custom error page. In fact, "error_page 404 /404.html" simply means use the 404.html file (default 404 file) in the nginx installation folder. this can as well be "error_page 404 /path/to/any/other/404.html" Alternatively, he could change the default file itself. – Dayo Sep 23 '11 at 16:04
  • 2
    The default 404 page isn't a file at all, and error_page does *not* take a filesystem path. It internally redirects to the given uri, so it goes looking for /404.html. It may be required to add location = /404.html { internal; } or similar to prevent the default 404 page from displaying, now that I think about it, because the internal redirect will end up right back in location / and I'm not sure what the return 404; will do there without recursive_error_pages on; (which would be bad anyway). – kolbyjack Sep 23 '11 at 17:08
  • "The default 404 page isn't a file at all" is incorrect. You can find it in the /usr/share/nginx/html/ folder (on redhat - may be different for others). The error_page directive takes a uri which can be under the webroot or aliased as needed. – Dayo Sep 23 '11 at 18:41
  • 3
    Perhaps redhat provides you with some generic error pages, but the native nginx error pages are generated by src/http/ngx_http_special_response.c. If you don't specify a custom error_page, that's what will generate the response. As I said before, error_page takes a uri argument, but both the OP and your original answer treated that argument as a filesystem path. – kolbyjack Sep 23 '11 at 19:13
  • I just copied his config at first and wasn't precise enough with it. I still can't see how your suggested configuration will give him a custom 404 page though which I thought was what he was looking for. I know the one I gave will do so since I use it for that very purpose (for all common error codes actually). – Dayo Sep 24 '11 at 05:36
  • 2
    The order of location and error_page doesn't matter, so just moving them around wasn't going to change anything. Both my config and your revised one return the error page in (almost) the same way. A request for /foo comes in, chooses location /, return 404 looks for an error page, finds it, performs an internal redirect to /404.html, which chooses location /404.html. That location uses the default (static) content handler, so it uses the root from the server and serves /opt/local/html/404.html with a 404 status. – kolbyjack Sep 24 '11 at 12:17
  • Got it now. I missed your "root /opt/local/html". I suppose the best one for him to choose will depend on his actual settings. I use the alias because my error docs live in one central location and every domain on the server accesses them there. More than one way to skin a cat. – Dayo Sep 24 '11 at 12:51
  • This worked for me. – jhyry-gcpud Dec 06 '21 at 19:57
3

Move the error_page directive up the conf to before you call return 404.

This should work:

# The default server.
#
server {
    listen       80 default_server;
    server_name  everythingelse;
    error_page 404 /error_docs/404.html;

    # Everything is a 404
    location / {
        return 404;
    }

    # Custom Error Page
    location /error_docs {
        alias /opt/local/html/;
        log_not_found  off;
        access_log off;
    }
}

This will use the same custom one for all sites (servers). You need to add the error docs location.

http {
error_page 404 /error_docs/404.html;

...

    # The default server.
    #
    server {
        listen       80 default_server;
        server_name  everythingelse;

        # Everything is a 404
        location / {
            return 404;
        }

        # Custom Error Page
        location /error_docs {
            alias /opt/local/html/;
            log_not_found  off;
            access_log off;
        }
    }
}
Dayo
  • 12,413
  • 5
  • 52
  • 67
2

In NGINX v1.14 (released in 2019-12-26) you cannot use location = /404.html. Removing the = (equals sign) works:

server {
    listen 80;

    server_name everythingelse;
    error_page 404 /404.html;

    location / {
        return 404;
    }

    location /404.html {
        root /opt/local/html;
    }
}
Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Deadelina
  • 41
  • 4
0

Since both root and error_page are valid directives at http block scope, one can leverage the inheritance behavior of nginx configuration.

To share custom error pages between all my vhosts (so that requests on unknown vhosts or inexistent resources in known vhosts get my custom error pages as a response according to error_page definition), I use the following recipe.

1. Add those three lines to /etc/nginx/nginx.conf

# …
root /var/www/whatever # Used by undefined hosts
error_page 403 404 =404 /404.html
error_page 502 503 504 =500 /500.html
# …

2. Create /etc/nginx/sites-available/catchall with the following snippet as a «catch all» default virtual server.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    # SSL rules here if required
    server_name _;
}

3. Create 404.html and 500.html files in each document root where a custom error has to be used (or link the ones in /var/www/whatever), otherwise defaults will be used instead.

That said, not all directives can inherit from a higher level scope, and even worse, some inheritance is unintuitive and doesn't behave as you might expect.

(Using Debian9 and nginx/1.10.3)

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Stphane
  • 3,368
  • 5
  • 32
  • 47
0

Before the server block, you are probably using the include instruction :

include /etc/nginx/conf.d/*.conf;
server {
    listen 80;
    ...
}

I had the same issue, and fixed it by removing the include line:

server {
    listen 80;
    ...
}
Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
CatSpy
  • 1