我在 Debian Bookworm 上运行默认的 nginx 包。通常我只使用 HTTPS 连接该服务,一切正常。HTTP 端口 80 的默认重定向到 HTTPS 协议。

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  server_tokens off;

  return 301 https://$host$request_uri;

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log error;
}

// vhosts with listen 443 configs omited here, they work

我有一个不支持 HTTPS 的 IoT 客户端。因此,我将其配置为使用 HTTP 将传感器数据发送到 Nginx 服务器上的挂钩 URL。我为该本地域 xyz 添加了 VHost,它不会将请求转发到 HTTPS,而是使用纯 HTTP 进行处理。

server {
  listen 80;
  listen [::]:80;
  server_name xyz;
  server_tokens off;

  location / {
    root /var/www/html/;
  }

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log error;
}

// vhosts with listen 443 configs omited here, they work

但是该请求从未被 Nginx 处理。

使用curl,我得到了这个回应

# curl -v http://localhost/
*   Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.88.1
> Accept: */*
> 
* Received HTTP/0.9 when not allowed
* Closing connection 0
curl: (1) Received HTTP/0.9 when not allowed

(Nginx 正在监听所有设备,没有防火墙处于活动状态,当我停止服务时,端口被关闭……)。

journald 中、nginx 访问或错误日志中都没有错误日志消息。

然后我尝试硬核telnet访问 80 端口,得到了更奇怪的结果:

# telnet localhost 80
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Connection closed by foreign host.

(要清楚,我输入时GET / HTTP/1.1\n没有第二个空行,只有一个“输入”,当我输入一些不符合 HTTP 规范的内容时,比如随机字符串,服务器会响应??

因此,在第一行之后,Nginx 就关闭了连接(HTTP/0.9 响应来自 curl 本身)。

因为我无法Host:在请求中设置标头,所以此请求永远不会检查任何虚拟主机配置。

我不知道哪里出了问题。服务器至少应该等待 Host-header 才能关闭连接。/etc/nginx/nginx.conf没有改变。

根据nginx -t配置,服务器在 上响应正常curl -v https://localhost/。SSL 配置等这里就省略了。它正在工作。

6

  • 你的问题很混乱。这里似乎没有任何 IOT 客户端,我们看到的唯一客户端是 curl。nginx 中的默认服务器没有 TLS 配置。在这里使用 301 很愚蠢。如果你的意思是神秘的 IOT 设备实际上是一个服务器,那么你应该向我们展示你为其添加了什么配置。


    – 

  • IoT 设备只是我需要纯 http 的一个原因。它调用一个钩子 URL。还有一条评论说,我剥离了 HTTPS 通信,因为我只在 HTTP 调用方面遇到问题。否则 curl 或 telnet 必须为默认服务器返回 301,但 Nginx 会立即关闭 http 连接而没有任何响应。


    – 

  • Because I could not set the Host: header in the request– 是的,你打了电话localhost


    – 

  • 不,telnet 命令没有设置任何内容。它只是打开套接字并发送GET / HTTP/1.1\n


    – 

  • 它看起来不像是 localhost:80 上的 nginx。我怀疑它能否用 http/0.9 应答或GET在行后立即关闭连接


    – 


最佳答案
1

如果根本没有日志消息,还有另一种方法可以找到问题。我们知道,普通的 Nginx 就可以正常工作。因此,从 nginx 中删除所有虚拟主机配置,然后从简单的默认设置开始。

通过这样做,我可以看到 Nginx 正在按预期做出响应。

然后将刚才的虚拟主机配置逐一添加。

问题出在另一个虚拟主机配置中,它在纯 http 套接字中使用了 http2 指令。

server {
  listen 80 http2;
  listen [::]:80 http2;
  server_name abc;
  server_tokens off;

  return 301 https://$host$request_uri;
}

(Debian Bookworm 上的旧 nginx 包(1.22.1)尚不支持http2 on;配置)

http2当删除此指令时,nginx / curl / telnet / IoT 将按预期工作。

当一个非 SSL 虚拟主机使用 HTTP/2 指令时,我找不到 Nginx 无法正常工作的原因。但我可以在其他几个系统上重现它。

文档

http2 参数 (1.9.5) 配置端口以接受 HTTP/2 连接。通常,要使其正常工作,还应指定 ssl 参数,但也可以配置 nginx 以接受没有 SSL 的 HTTP/2 连接。

4

  • 哦,我明白了。listen 指令上的标志对于端口号来说是全局的,因此所有监听端口 80 的服务器都将使用 http2。这不是一个显而易见的知识。


    – 

  • 无论如何,没有 ssl 的 http2 几乎毫无意义,没有真正的浏览器支持它。


    – 

  • 真的吗?所以一个 listen 443 ssl 就足够了,其他所有 listen 443 都需要设置 SSL,即使没有该指令也是如此。哦。我还以为 http2 指令向后兼容 http1。


    – 

  • 是的,普通端口上的 http2 不向后兼容。它在 https 上兼容,因为浏览器和服务器可以在 http 之前使用 ALPN 协商协议。


    –