为什么nginx会响应任何域名?

Mar*_*tin 133 nginx

我有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
Run Code Online (Sandbox Code Playgroud)

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

Day*_*ayo 193

nginx配置中的第一个服务器块是所有到达没有特定服务器块的服务器的请求的默认值.

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

这就是为什么正确的Nginx配置具有特定服务器块的默认值,然后与其他特定域一起使用.

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}
Run Code Online (Sandbox Code Playgroud)

等等

**编辑**

似乎有些用户对此示例感到有点困惑,并认为它仅限于一个conf文件等.

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

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

http {
    [...]
    # Default server
    server {
        return 404;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}
Run Code Online (Sandbox Code Playgroud)

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

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

此外,这种安排提供了很大的灵活性,因为可以定义多个默认值.

在我的具体情况下,我只在内部接口上监听端口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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 静态,请参阅Oleg Neumyvkin的回答 - 如果您在站点中有多个配置文件 - 可用,则按字母顺序排列第一个文件中的第一个服务器是您的默认设置.我怀疑这可能是一个问题.此外,许多发行版在重新启动之前运行'nginx -t'来测试配置 - 您可能会遇到阻止重新启动的错误. (2认同)
  • 这是不完整的,不应该是公认的答案.为此,您还必须从任何listen指令中删除default_server. (2认同)

iTe*_*ech 59

您应该有一个默认服务器用于catch-all,您可以返回404或更好地完全不响应(将节省一些带宽)返回444哪个是nginx特定的HTTP响应,只需关闭连接并且不返回任何内容

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}
Run Code Online (Sandbox Code Playgroud)

  • @iTech:由于某种原因,它为所有请求返回444.有线索吗? (2认同)
  • 关于“444”的重要提示,恕我直言,这是一个比返回错误代码更干净的解决方案。 (2认同)

Mat*_*ier 33

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

server {
    listen 443;
    server_name example.com;

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

    ...
}
Run Code Online (Sandbox Code Playgroud)

  • @Esolitos从字面上看,该文章的第三行说这个用例还可以。我知道需要谨慎,但不要在合理的用例上摇摆不定。 (2认同)

Pav*_*vel 26

指定默认服务器的方法很少.

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

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

server {
    listen  *:80 default_server;
    root /www/project/public/;
}
Run Code Online (Sandbox Code Playgroud)

更多信息:Nginx doc/Listen

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

  • 这应该是正确的答案.接受的答案是误导性的.如果将另一台服务器配置为默认服务器,则只需在配置中添加另一台服务器就无法解决问题. (3认同)

小智 22

要回答你的问题 - 如果没有匹配,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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这似乎是大多数配置的最佳答案......现在不确定谁在使用没有 SSL 的 nginx,但这是唯一一个涵盖 SSL 的事实非常有说服力。 (4认同)
  • 我不知道为什么这个答案如此之低。这是一个回答问题而又不会被过程中闪亮的事物分散注意力的人。 (3认同)

Ole*_*kin 8

回答的评论很少:

如果您在sites-available /中的多个配置文件中的多个IP上有多个虚拟主机,则IP的"默认"域将按字母顺序从第一个文件中获取.

正如Pavel所说,"listen"指令有"default_server"参数http://nginx.org/en/docs/http/ngx_http_core_module.html#listen


Кон*_*Ван 6

server {\n    listen 80 default_server;\n    listen [::]:80 default_server;\n    listen 443 default_server;\n    listen [::]:443 default_server;\n    return 444;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

如果您只是想忽略此类带有不需要的请求HostHost根本没有请求,请创建一个接收器服务器,每个可能的address:port默认对都将其设置为接收器服务器,然后return 444;在那里执行。

\n

不必为接收器支持 HTTP/2、HTTP/3 或 SSL 连接而烦恼。对于不支持的连接,服务器会拒绝此类连接。

\n

对于受支持类型的连接,它\xe2\x80\x99将立即挂起连接,而无需发送响应,这就是return 444;所做的事情。

\n
\n

从文档中

\n

http://nginx.org/en/docs/http/request_processing.html

\n
\n

...NGINX 仅测试 request\xe2\x80\x99s 标头字段 \xe2\x80\x9c Host\xe2\x80\x9d 以确定请求应路由到哪个服务器。如果其值与任何服务器名称都不匹配,或者请求根本不包含此标头字段,则 NGINX 会将请求路由到此端口的默认服务器。

\n
\n

https://nginx.org/en/docs/http/ngx_http_core_module.html#listen

\n
\n

...\xc2\xa0 default_server\xc2\xa0 参数(如果存在)将导致服务器成为指定 \xc2\xa0 address:port\xc2\xa0 对的默认服务器。

\n
\n