Nginx为什么响应任何域名?


139

我已经安装了nginx并使用Ruby / Sinatra应用程序运行,一切都很好。但是,我现在试图在同一台服务器上运行第二个应用程序,但我发现有些奇怪。首先,这是我的nginx.conf:

pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
  worker_connections 1024;
  accept_mutex off;
}

http {
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  sendfile on;
  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream app {
    server unix:/var/www/app/tmp/sockets/unicorn.sock fail_timeout=0;
  }

  server {
    listen 80;
    client_max_body_size 4G;
    server_name FAKE.COM;

    keepalive_timeout 5;

    root /var/www/app/public;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      if (!-f $request_filename) {
        proxy_pass http://app;
        break;
      }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/app/public;
    }
  }
}
                                                          68,0-1        B

请注意,如何server_name设置为FAKE.COM服务器如何响应通过其他域名访问该服务器的所有主机。如何使特定服务器仅响应对的请求FAKE.COM


listen fake.com | something.com:80 命令过滤器,不是server_name
阿列克谢·马尔琴科

Answers:


202

nginx配置中的第一个服务器块是命中该服务器的所有请求(没有特定服务器块)的默认请求。

因此,在您的配置中,假设您的真实域是REAL.COM,则当用户键入该域名时,它将解析到您的服务器,并且由于没有用于此设置的服务器块,因此FAKE.COM的服务器块是第一个服务器块(在您的情况下仅服务器块)将处理该请求。

这就是为什么正确的Nginx配置在默认域之前具有特定于默认值的特定服务器块的原因。

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}

等等

**编辑**

看来有些用户对此示例有些困惑,并认为它仅限于单个conf文件等。

请注意,以上是根据需要开发OP的简单示例。

我个人这样使用单独的vhost conf文件(CentOS / RHEL):

http {
    [...]
    # Default server
    server {
        return 404;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/ 将包含domain_1.conf,domain_2.conf ... domain_n.conf,它将包含在主nginx.conf文件中的服务器块之后,该文件将始终是第一个,并且始终是默认值,除非使用default_server覆盖它指令在其他地方。

在这种情况下,其他服务器的conf文件的文件名的字母顺序变得无关紧要。

另外,这种安排具有很大的灵活性,因为可以定义多个默认值。

在我的特定情况下,我让Apache仅在内部接口上侦听端口8080,然后将PHP和Perl脚本代理到Apache。

但是,我运行了两个单独的应用程序,它们都在附带的输出html中返回带有“:8080”的链接,因为它们检测到Apache没有在标准端口80上运行,并尝试“帮助”我。

这将导致一个问题,即链接变得无效,因为无法从外部接口访问Apache,并且链接应指向端口80。

我通过为端口8080创建默认服务器来重定向此类请求来解决此问题。

http {
    [...]
    # Default server block for undefined domains
    server {
        listen 80;
        return 404;
    }
    # Default server block to redirect Port 8080 for all domains
    server {
        listen my.external.ip.addr:8080;
        return 301 http://$host$request_uri;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

由于常规服务器块中没有任何内容侦听端口8080,因此重定向默认服务器块凭借其在nginx.conf中的位置来透明地处理此类请求。

我实际上有四个这样的服务器块,这是一个简化的用例。


1
Nginx要求区分大小写-“服务器”应为服务器,“返回”应为返回。希望这可以节省复制此代码时的一些麻烦。
Capaj

2
静态,请参阅Oleg Neumyvkin的答案-如果站点中有多个配置文件可用,则默认情况下,按字母顺序在第一个文件中的第一个服务器是默认设置。我怀疑这可能是个问题。同样,许多发行版在重新启动之前运行'nginx -t'来测试配置-您可能会遇到阻止重新启动的错误。
jwhitlock

2
这是不完整的,不应被接受。为此,您还必须从任何侦听指令中删除default_server。

@ben首先,OP在他的问题示例中没有“ default_server”,并且答案针对该问题的具体情况而定制。第二,为什么当有人按照说明将第一个定义的服务器设置为默认服务器时,为什么有人会在单独的服务器位置上定义default_server?无论如何,如果已经专门定义了default_server,则不应考虑使用此Q / A集来解决它们可能遇到的任何问题。
Dayo 2014年

1
@Dayo您正确地解决了OP发布的特定配置。但是对于所问问题的完整答案,我认为有必要提及default_server。很可能阅读您的答案而没有意识到default_server将如何干扰它。甚至更有可能是因为某些发行版中在文件中定义了default_server的发行版对用户来说可能并不明显。
2014年

61

您应该有一个默认服务器以实现包罗万象,您可以返回404或更佳的选择是根本不响应(将节省一些带宽),方法是返回444nginx特定的HTTP响应,该响应只是关闭连接而什么也不返回

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}

为我工作的Nginx 1.8.0。在server_name _;我没有在早期版本的nginx的,但它的工作。现在,对于较新的nginx版本,您似乎需要server_name _;。谢谢
丹尼尔(Daniel)2015年

解决了。我忘记了分号; _;
user1201917'7

2
@iTech:由于某种原因,它为所有请求返回444。有什么线索吗?
Divick

关于的重要提示444,并且imho比返回错误代码更干净的解决方案。
qqilihq

1
指定证书/密钥很重要,否则所有的SSL连接都会匹配并失败,如@AndreyT在下面的回答中所指出的。
马克·弗莱彻

34

我无法用其他任何答案解决问题。我通过检查主机是否匹配并返回403(如果不匹配)来解决了该问题。(我有一些随机网站指向我的Web服务器内容。我想劫持搜索排名)

server {
    listen 443;
    server_name example.com;

    if ($host != "example.com") {
        return 403;
    }

    ...
}

1
如果是不好的做法,请访问:nginx.com/resources/wiki/start/topics/depth/ifisevil
Esolitos

1
在某些情况下,您根本无法避免使用if,例如,如果您需要测试没有等效指令的变量。
爱德华

4
@Esolitos从字面上看,该文章的第三行说这个用例还可以。我知道需要谨慎,但不要在合理的用例上摇摆不定。
大功率

在我看来,这是最简单,最简洁的解决方案,因为它只需要3行代码,您就可以欣然将其粘贴到任何vhost文件中,之后只需更改比较的$ host即可。
Akito

28

要回答您的问题-如果没有匹配项,nginx会选择第一个服务器。参见文档

如果其值与任何服务器名称都不匹配,或者请求根本不包含此标头字段,则nginx会将请求路由到该端口的默认服务器。在上面的配置中,默认服务器是第一台服务器。

现在,如果您想拥有一个默认的全部捕获服务器,例如,它对所有请求均以404进行响应,那么这是这样做的方法:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_certificate <path to cert>
    ssl_certificate_key <path to key>
    return 404;
}

请注意,您需要指定证书/密钥(可以是自签名的),否则所有的SSL连接都会失败,因为nginx会尝试使用此default_server接受连接,并且找不到证书/密钥。


3
我不知道为什么这个答案如此之低。这是一个回答问题而又不会被过程中闪亮的事物分散注意力的人。
mmc

这帮助我解决了一个问题,当我尝试从非www重定向到www时,我必须在此重定向路由中包括我的ssl证书,否则正在尝试获取我的默认ssl证书,该证书用于其他域。
endyourif

1
对于大多数配置来说,这似乎是最好的答案……不确定这些天谁在使用不带SSL的nginx,但这是唯一涵盖SSL的事实,这非常有道理。
mpowered

似乎server_name _;甚至不需要。
朱利安·萨利纳斯

26

有几种方法可以指定默认服务器。

第一种方法 -如果将服务器配置保存在一个配置文件中(例如上面显示的Dayo),则在列表中首先指定默认服务器。

第二种方式(更好)更灵活- default_serverlisten指令提供参数,例如:

server {
    listen  *:80 default_server;
    root /www/project/public/;
}

此处了解更多信息:Nginx doc /听

当您将服务器配置保存在单独的文件中并且不想按字母顺序命名这些文件时,这种方式会更加有用。


3
这应该是正确的答案。公认的答案具有误导性。如果将另一台服务器配置为默认服务器,仅将另一台服务器添加到配置中将无法解决问题。
2014年

1
@Pavel没有理由说明提供的答案不能与多个单独的vhost文件一起使用。另外,据此,我知道我的默认服务器始终位于主nginx.conf文件中,而不必记住许多单独的vhost文件中的哪一个包含此文件。
Dayo 2014年

不要忘记也为ssl收听443(请参见下面的@AndreyT的回复)。
君士坦丁堡州

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.