要配置 HTTPS 服务器,必须在服务器块中的侦听套接字上启用该参数,并且应指定服务器证书和私钥文件的位置:
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 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
}
服务器证书是一个公共实体。它被发送到连接到服务器的每个客户端。私钥是一个安全的实体,应该存储在一个访问受限的文件中,但是,它必须可以被nginx的主进程读取。私钥可以交替地存储在与证书相同的文件中:
ssl_certificate www.example.com.cert;
ssl_certificate_key www.example.com.cert;
在这种情况下,文件访问权限也应受到限制。尽管证书和密钥存储在一个文件中,但只有证书发送到客户端。
ssl_protocols和ssl_ciphers指令可用于限制连接,使其仅包含 SSL/TLS 的强版本和密码。默认情况下,nginx使用“”和“”,因此通常不需要显式配置它们。请注意,这些指令的默认值已更改多次。ssl_protocols TLSv1 TLSv1.1 TLSv1.2ssl_ciphers HIGH:!aNULL:!MD5
HTTPS 服务器优化
SSL 操作会消耗额外的 CPU 资源。在多处理器系统上,应运行几个工作进程,不少于可用 CPU 内核的数量。CPU 占用最多的操作是 SSL 握手。有两种方法可以最大限度地减少每个客户端的这些操作的数量:第一种方法是启用保持活动连接以通过一个连接发送多个请求,第二种方法是重用 SSL 会话参数以避免并行和后续连接的 SSL 握手。会话存储在工作线程之间共享并由 ssl_session_cache 指令配置的 SSL 会话缓存中。一兆字节的缓存包含大约 4000 个会话。默认缓存超时为 5 分钟。可以通过使用ssl_session_timeout指令来增加它。下面是针对具有 10 MB 共享会话缓存的多核系统优化的示例配置:
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 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
...
证书链
某些浏览器可能会抱怨由知名证书颁发机构签名的证书,而其他浏览器可能会毫无问题地接受该证书。发生这种情况是因为颁发机构使用中间证书对服务器证书进行了签名,该中间证书不存在于使用特定浏览器分发的已知受信任证书颁发机构的证书库中。在这种情况下,颁发机构提供一堆链接的证书,这些证书应连接到签名的服务器证书。服务器证书必须出现在组合文件中链接的证书之前:
$ cat www.example.com.crt bundle.crt > www.example.com.chained.crt
生成的文件应在 ssl_certificate 指令中使用:
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:0B080074:x509 certificate routines:
X509_check_private_key:key values mismatch)
因为nginx试图将私钥与捆绑包的第一个证书一起使用,而不是服务器证书。
浏览器通常存储它们接收的中间证书,这些证书由受信任的颁发机构签名,因此主动使用的浏览器可能已经具有所需的中间证书,并且可能不会抱怨在没有链接捆绑包的情况下发送的证书。为了确保服务器发送完整的证书链,可以使用命令行实用程序,例如:openssl
$ 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
…
使用 SNI 测试配置时,请务必指定该选项,因为默认情况下不使用 SNI。-servernameopenssl
在此示例中,服务器证书 #0 的使用者 (“s”) 由颁发者 (“i”) 签名,该颁发者本身就是证书 #1 的使用者,颁发者本身就是证书 #2 的使用者,该颁发者由著名的颁发者 ValiCert, Inc. 签名,其证书存储在浏览器的内置证书库中(位于 Jack 构建的房屋中)。www.GoDaddy.com
如果尚未添加证书捆绑包,则仅显示服务器证书 #0。
单个 HTTP/HTTPS 服务器
可以配置同时处理 HTTP 和 HTTPS 请求的单个服务器:
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
基于名称的 HTTPS 服务器
在单个 IP 地址上配置两个或多个 HTTPS 服务器侦听时,会出现一个常见问题:
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;
…
}
使用此配置,浏览器接收默认服务器的证书,即 无论请求的服务器名称如何。这是由 SSL 协议行为引起的。SSL连接是在浏览器发送HTTP请求之前建立的,nginx不知道所请求服务器的名称。因此,它可能只提供默认服务器的证书。www.example.com
解决此问题的最古老,最可靠的方法是为每个HTTPS服务器分配一个单独的IP地址:
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;
…
}