173

I have an instance of nginx running which serves several websites. The first is a status message on the server's IP address. The second is an admin console on admin.domain.com. These work great. Now I'd like all other domain requests to go to a single index.php - I have loads of domains and subdomains and it's impractical to list them all in an nginx config.

So far I've tried setting server_name to * but that failed as an invalid wildcard. *.* works until I add the other server blocks, then I guess it conflicts with them.

Is there a way to run a catch-all server block in nginx after other sites have been defined?

N.B. I'm not a spammer, these are genuine sites with useful content, they're just powered by the same CMS from a database!

Tak
  • 11,428
  • 5
  • 29
  • 48

8 Answers8

193

Change listen option to this in your catch-all server block. (Add default_server) this will take all your non-defined connections (on the specified port).

listen       80  default_server;

if you want to push everything to index.php if the file or folder does not exist;

try_files                       $uri /$uri /index.php;

Per the docs, It can also be set explicitly which server should be default, with the **default_server** parameter in the listen directive

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Mattias
  • 9,211
  • 3
  • 42
  • 42
  • 1
    Worked perfectly - many thanks. I couldn't use `server_name _;` for the status page on the IP address, I had to specify `server_name x.x.x.x` but that's okay! – Tak Feb 26 '12 at 17:11
  • 1
    Adding `default_server` doesn't seem to work on nginx 1.4.6 which is currently the latest version on Ubuntu 14.04 ... When I add it, the `configtest` command returns an error, and restarting the server doesn't work either. I've tried the exact same config on my server with Debian jessie, which has nginx 1.6.2, and it works perfectly. So try another version if you're on 1.4.6 ... – Nicomak Feb 22 '16 at 03:21
  • 4
    Side note for HTTPS: the ```default_server``` directive also sets the server that will handle the SSL handshake for requests on that port. So, if you want server block A to handle SSL, but server B to act as the catchall for HTTPS, the solution is to set ```server_name ~^(.+)$``` on server B. – Luke Mar 20 '17 at 06:16
110

As a convention, the underscore is used as a server name for default servers.

From http://nginx.org/en/docs/http/server_names.html

In catch-all server examples the strange name “_” can be seen:

server {
   listen       80  default_server;
   server_name  _;
   return       444;
}

There is nothing special about this name, it is just one of a myriad of >invalid domain names which never intersect with any real name. Other >invalid names like “--” and “!@#” may equally be used.

Note that server_name _; alone is not enough. The above example only works because of default_server in the listen directive.

idmean
  • 14,540
  • 9
  • 54
  • 83
jp.gouigoux
  • 1,301
  • 1
  • 8
  • 3
  • 63
    This answer is not true. The reference makes it clear that this will not work unless you also have `listen 80 default_server` in your config. – Beetle Jun 12 '15 at 09:35
  • 6
    Also see [this article](http://blog.gahooa.com/2013/08/21/nginx-how-to-specify-a-default-server/). – Beetle Jun 12 '15 at 09:42
  • 4
    Maybe I misunderstood the docs, but "There is nothing special about this name, it is just one of a myriad of invalid domain names which never intersect with any real name. Other invalid names like “--” and “!@#” may equally be used.". So `_` is just an **invalid** name ? – Florian Klein Mar 25 '17 at 12:54
  • 3
    Yes, *it is an invalid* config. It is not a wildcard. – agoldev May 03 '19 at 10:49
  • 6
    I wonder why do we need this _invalid_ config when we have `listen` directive with `default_server` parameter? What's the purpose of the `server_name _;` directive? – Psylone Feb 03 '20 at 00:44
  • 10
    @Psylone You can ommit the `server_name` directive. In current versions of nginx it will then use the empty name - which will work perfectly. The invalid `server_name` is a recommendation from old times: verisons of nginx up to 0.8.48 used the hostname of your machine if no `server_name` was specified in a `server` block, which might not be desired. Setting it to something invalid fixed that. – Johannes H. May 12 '20 at 09:59
  • I used exactly this config and nginx doesn't work at all. It returns `ERR_CONNECTION_REFUSED` – James Bond Jan 17 '22 at 20:58
39

This will work:

server_name ~^(.+)$
itsjeyd
  • 5,070
  • 2
  • 30
  • 49
Tim Kozak
  • 4,026
  • 39
  • 44
18

Now you can use mask:

server {
    listen       80;
    server_name  *.example.org;
    ...
}

server {
    listen       80;
    server_name  mail.*;
    ...
}

Look more here: http://nginx.org/en/docs/http/server_names.html

redflasher
  • 677
  • 9
  • 17
17

Only 1 server directive

From Nginx listen Docs

The default_server parameter, if present, will cause the server to become the default server for the specified address:port pair. If none of the directives have the default_server parameter then the first server with the address:port pair will be the default server for this pair.

If you only have 1 server directive, that will handle all request, you don't need to set anything.


Multiple server directive

If you want to match all request with specified server directive, just add default_server parameter to listen, Nginx will use this server directive as default.

server {
    listen 80 default_server;
}

About server_name _;

From Nginx Docs

In catch-all server examples the strange name “_” can be seen:

server {
    listen       80  default_server;
    server_name  _;
    return       444;
}

There is nothing special about this name, it is just one of a myriad of invalid domain names which never intersect with any real name. Other invalid names like “--” and “!@#” may equally be used.

It doesn't matter what server_name you set, it is just an invalid domain name.

Steely Wing
  • 16,239
  • 8
  • 58
  • 54
14

For me somehow define default_server was not working. I solved it by

server_name ~^.*$

using regular expression of all.

Ashish
  • 1,171
  • 11
  • 12
  • 1
    Tried all higher voted options and this is the only one that worked. NOTE: I'm trying to get to a server with it's IP address vs. URL. – Mampersat Jul 20 '16 at 23:03
  • 1
    In nginx 1.20.2 it works with a ; at the end `server_name ~^.*$;` – Narkha Dec 20 '21 at 19:05
5

If you also want to catch requests with empty Host header (which is allowed in HTTP/1.0) you can use both regex and empty server_name:

server {
    listen      80;
    server_name ~. "";
}
Schtolc
  • 1,036
  • 1
  • 12
  • 19
-1

Try $http_host

server {
    server_name $http_host;
}
Abhishek Gurjar
  • 7,426
  • 10
  • 37
  • 45
Balaji
  • 9,657
  • 5
  • 47
  • 47