Appearance
配置 HTTPS 服务器
提示
来自deepseek解释
原文链接:https://nginx.org/en/docs/http/configuring_https_servers.html
要配置 HTTPS 服务器,必须在 server 块的监听套接字上启用 ssl 参数,并指定服务器证书和私钥文件的位置:
nginx
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}服务器证书是一个公共实体,会发送给每个连接到服务器的客户端。私钥是安全实体,应存储在访问受限的文件中,但必须能被 nginx 的主进程读取。私钥也可以与证书存储在同一个文件中:
nginx
ssl_certificate www.example.com.cert;
ssl_certificate_key www.example.com.cert;在这种情况下,文件访问权限也应加以限制。尽管证书和密钥存储在一个文件中,但只有证书会被发送给客户端。
可以使用 ssl_protocols 和 ssl_ciphers 指令来限制连接仅使用强版本的 SSL/TLS 协议及加密套件。默认情况下,nginx 使用 ssl_protocols TLSv1.2 TLSv1.3 和 ssl_ciphers HIGH:!aNULL:!MD5,因此通常不需要显式配置它们。请注意,这些指令的默认值曾多次变更。
HTTPS 服务器优化
SSL 操作会消耗额外的 CPU 资源。在多处理器系统上,应运行多个工作进程,数量不少于可用 CPU 核心数。
最消耗 CPU 资源的操作是 SSL 握手。有两种方法可以最小化每个客户端的这些操作次数:第一种是启用 keepalive 连接,通过一个连接发送多个请求;第二种是重用 SSL 会话参数,以避免并行和后续连接的 SSL 握手。
会话存储在工作进程间共享的 SSL 会话缓存中,由 ssl_session_cache 指令配置。1 MB 的缓存大约可以存储 4000 个会话。默认缓存超时为 5 分钟,可以通过 ssl_session_timeout 指令增加。
以下是一个针对多核系统优化的配置示例,使用 10 MB 共享会话缓存:
nginx
worker_processes auto;
http {
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
server {
listen 443 ssl;
server_name www.example.com;
keepalive_timeout 70;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
}SSL 证书链
有些浏览器可能会对由知名证书颁发机构(CA)签名的证书发出警告,而其他浏览器则可能正常接受该证书。这是因为颁发机构使用了一个中间证书来签署服务器证书,而该中间证书不在特定浏览器所分发的知名受信任 CA 证书库中。
在这种情况下,证书颁发机构会提供一个证书链捆绑包,应将其与已签名的服务器证书合并。服务器证书必须放在合并文件中所有链式证书之前:
bash
$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt生成的文件应在 ssl_certificate 指令中使用:
nginx
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.chained.crt;
ssl_certificate_key www.example.com.key;
...
}如果服务器证书和捆绑包的合并顺序错误,nginx 将无法启动并显示如下错误信息:
SSL_CTX_use_PrivateKey_file(" ... /www.example.com.key") failed (SSL: error:05800074:x509 certificate routines::key values mismatch)这是因为 nginx 尝试使用私钥去匹配捆绑包中的第一个证书(而非服务器证书)。
浏览器通常会存储它们接收到的、由受信任机构签名的中间证书,因此常用的浏览器可能已经拥有了所需的中间证书,即使没有发送证书链捆绑包也不会发出警告。
为确保服务器发送完整的证书链,可以使用 openssl 命令行工具,例如:
bash
$ openssl s_client -connect www.godaddy.com:443
...
Certificate chain
0 s:/C=US/ST=Arizona/L=Scottsdale/1.3.6.1.4.1.311.60.2.1.3=US
/1.3.6.1.4.1.311.60.2.1.2=AZ/O=GoDaddy.com, Inc /OU=MIS
Department/CN=www.GoDaddy.com /serialNumber=0796928-7/2.5.4.15=V1.0, Clause
5.(b)
i:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
/OU=http://certificates.godaddy.com/repository /CN=Go Daddy Secure Certification
Authority /serialNumber=07969287
1 s:/C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc.
/OU=http://certificates.godaddy.com/repository /CN=Go Daddy Secure Certification
Authority /serialNumber=07969287
i:/C=US/O=The Go Daddy Group, Inc. /OU=Go Daddy Class 2 Certification Authority
2 s:/C=US/O=The Go Daddy Group, Inc.
/OU=Go Daddy Class 2 Certification Authority
i:/L=ValiCert Validation Network/O=ValiCert, Inc.
/OU=ValiCert Class 2 Policy Validation Authority
/CN=http://www.valicert.com//emailAddress=info@valicert.com
...在此示例中,www.GoDaddy.com 服务器证书 #0 的持有者(subject,“s”)由签发者(issuer,“i”)签名,而该签发者本身又是证书 #1 的持有者,#1 由另一个签发者签名,该签发者本身又是证书 #2 的持有者,#2 由知名签发机构 ValiCert, Inc. 签名,其证书存储在浏览器内置的证书库中。
如果未添加证书捆绑包,则只会显示服务器证书 #0。
单台 HTTP/HTTPS 服务器
可以配置一台同时处理 HTTP 和 HTTPS 请求的服务器:
nginx
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
...
}在 0.7.14 版本之前,SSL 不能像上面那样针对单个监听套接字选择性启用。SSL 只能通过 ssl 指令为整个服务器启用,这使得无法设置单一的 HTTP/HTTPS 服务器。listen 指令的 ssl 参数正是为了解决此问题而添加的。因此,在现代版本中不推荐使用 ssl 指令;它已在 1.25.1 版本中移除。
基于域名的 HTTPS 服务器
在配置两个或多个 HTTPS 服务器监听同一 IP 地址时,经常会出现一个常见问题:
nginx
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
...
}
server {
listen 443 ssl;
server_name www.example.org;
ssl_certificate www.example.org.crt;
...
}在这种配置下,无论请求的服务器名称是什么,浏览器都会收到默认服务器的证书(即 www.example.com)。这是由 SSL 协议的行为导致的——SSL 连接在浏览器发送 HTTP 请求之前就已建立,此时 nginx 并不知道所请求的服务器名称,因此只能提供默认服务器的证书。
解决此问题最古老也最可靠的方法是为每个 HTTPS 服务器分配独立的 IP 地址:
nginx
server {
listen 192.168.1.1:443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
...
}
server {
listen 192.168.1.2:443 ssl;
server_name www.example.org;
ssl_certificate www.example.org.crt;
...
}包含多个域名的 SSL 证书
还有其他方法可以在多个 HTTPS 服务器之间共享单个 IP 地址,但各有其缺点。
一种方法是使用包含多个名称的证书,在 SubjectAltName 证书字段中列出,例如 www.example.com 和 www.example.org。但 SubjectAltName 字段的长度是有限的。
另一种方法是使用通配符证书,例如 *.example.org。通配符证书可以保护指定域名的所有子域名,但仅限于一级。该证书匹配 www.example.org,但不匹配 example.org 和 www.sub.example.org。
这两种方法也可以结合使用。证书可以在 SubjectAltName 字段中同时包含精确名称和通配符名称,例如 example.org 和 *.example.org。
最好将包含多个名称的证书文件及其私钥文件放在 http 级别配置中,以便在所有服务器中继承其单一内存副本:
nginx
ssl_certificate common.crt;
ssl_certificate_key common.key;
server {
listen 443 ssl;
server_name www.example.com;
...
}
server {
listen 443 ssl;
server_name www.example.org;
...
}服务器名称指示(SNI)
在单个 IP 地址上运行多个 HTTPS 服务器的更通用解决方案是 TLS 服务器名称指示扩展(SNI,RFC 6066),它允许浏览器在 SSL 握手期间传递所请求的服务器名称,从而使服务器知道该为连接使用哪个证书。
SNI 目前已被大多数现代浏览器支持,并且在 TLSv1.3 中是必须实现的扩展,但某些旧版或特殊客户端可能不支持。SNI 中只能传递域名,但如果请求中包含字面 IP 地址,某些浏览器可能会错误地将服务器的 IP 地址作为名称传递——不应依赖于此行为。
要在 nginx 中使用 SNI,必须同时满足以下两个条件:构建 nginx 二进制文件时所使用的 OpenSSL 库支持 SNI,以及运行时动态链接的 OpenSSL 库也支持 SNI。OpenSSL 自 0.9.8f 版本起支持 SNI(如果编译时使用了配置选项):
bash
$ nginx -V
...
TLS SNI support enabled
...然而,如果支持 SNI 的 nginx 动态链接到不支持 SNI 的 OpenSSL 库,nginx 会显示警告:
nginx was built with SNI support, however, now it is linked dynamically to an OpenSSL library which has no tlsext support, therefore SNI is not available兼容性
- 自 0.8.21 和 0.7.62 版本起,
-V开关会显示 SNI 支持状态。 listen指令的ssl参数自 0.7.14 版本起支持。在 0.8.21 版本之前,它只能与default参数一起指定。- SNI 自 0.5.23 版本起支持。
- 共享 SSL 会话缓存自 0.5.6 版本起支持。
- 1.27.3 及更高版本:默认 SSL 协议为 TLSv1.2 和 TLSv1.3(如果 OpenSSL 库支持)。否则,当使用 OpenSSL 1.0.0 或更早版本时,默认 SSL 协议为 TLSv1 和 TLSv1.1。
- 1.23.4 及更高版本:默认 SSL 协议为 TLSv1、TLSv1.1、TLSv1.2 和 TLSv1.3(如果 OpenSSL 库支持)。
- 1.9.1 及更高版本:默认 SSL 协议为 TLSv1、TLSv1.1 和 TLSv1.2(如果 OpenSSL 库支持)。
- 0.7.65、0.8.19 及更高版本:默认 SSL 协议为 SSLv3、TLSv1、TLSv1.1 和 TLSv1.2(如果 OpenSSL 库支持)。
- 0.7.64、0.8.18 及更早版本:默认 SSL 协议为 SSLv2、SSLv3 和 TLSv1。
- 1.0.5 及更高版本:默认 SSL 加密套件为
HIGH:!aNULL:!MD5。 - 0.7.65、0.8.20 及更高版本:默认 SSL 加密套件为
HIGH:!ADH:!MD5。 - 0.8.19 版本:默认 SSL 加密套件为
ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM。 - 0.7.64、0.8.18 及更早版本:默认 SSL 加密套件为
ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP。
原文作者:Igor Sysoev,编辑:Brian Mercer