在只有一个公网 IP 的情况下托管多个 HTTPS 站点,并为每个站点使用不同的 SSL 证书,这是典型的单 IP 多域名 SSL 场景。该方案依赖于 SNI(Server Name Indication) 扩展,它允许服务器在 TLS 握手阶段根据客户端发送的域名来选择对应的证书。Nginx 对 SNI 的支持非常完善,配置也十分简单。下面是一套完整的 Nginx 配置方案,包含站点隔离、证书指定、HTTP 强跳 HTTPS 以及性能优化建议。
1. 前提条件
- Nginx 版本 ≥ 0.8.21(SNI 功能从该版本开始稳定)。
- 编译 Nginx 时已开启 SNI 支持(可通过 `nginx -V` 检查输出中是否有 `TLS SNI support enabled`)。
- 已为每个域名申请好 SSL 证书(通常包含 `.crt` 证书文件和 `.key` 私钥文件)。
2. 基本配置结构
在 Nginx 的配置文件中(例如 `/etc/nginx/nginx.conf` 或 `/etc/nginx/sites-enabled/` 下的文件),为每个 HTTPS 站点定义一个 `server` 块,均监听 `443 ssl`,并通过 `server_name` 区分不同域名。
nginx
全局 SSL 配置(可放在 http 块内,作为默认值)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
站点 A
server {
listen 443 ssl;
server_name www.example.com example.com;
ssl_certificate /path/to/example.com/fullchain.pem; # 证书文件(含中间证书)
ssl_certificate_key /path/to/example.com/privkey.pem; # 私钥文件
其他站点配置(根目录、日志等)
root /var/www/example;
index index.html;
}
站点 B
server {
listen 443 ssl;
server_name www.anotherexample.com anotherexample.com;
ssl_certificate /path/to/anotherexample.com/fullchain.pem;
ssl_certificate_key /path/to/anotherexample.com/privkey.pem;
root /var/www/anotherexample;
index index.html;
}
关键点:
- 每个 `server` 块独立指定 `ssl_certificate` 和 `ssl_certificate_key`。
- 多个 `server` 块共享同一个 IP 的 `443` 端口,Nginx 会根据 `server_name` 自动选择匹配的证书。
3. 将 HTTP 请求重定向到 HTTPS
为了让用户访问 `http://` 时自动跳转到 `https://`,需要为每个站点额外添加一个监听 `80` 端口的 `server` 块,并返回 301 重定向。
nginx
HTTP 重定向(可以统一写一个 server 块匹配所有域名,也可以为每个域名单独写)
server {
listen 80;
server_name www.example.com example.com www.anotherexample.com anotherexample.com;
统一重定向到 HTTPS 相同域名
return 301 https://$host$request_uri;
}
或者为了更清晰,也可以为每个站点分别写:
nginx
server {
listen 80;
server_name www.example.com example.com;
return 301 https://$host$request_uri;
}
server {
listen 80;
server_name www.anotherexample.com anotherexample.com;
return 301 https://$host$request_uri;
}
4. 优化与注意事项
4.1 SSL 会话缓存复用
在 `http` 块中设置的 `ssl_session_cache` 可以让所有站点共享 SSL 会话缓存,提高 TLS 握手性能。建议配置如下:
```nginx
ssl_session_cache shared:SSL:50m; # 大小根据站点数量调整
ssl_session_timeout 4h;
4.2 使用 HSTS(可选)
如果希望强制浏览器始终使用 HTTPS,可以在 HTTPS 站点中添加 HSTS 头:
```nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
4.3 证书路径与权限
- 确保证书文件和私钥文件对 Nginx 进程(如 `www-data` 或 `nginx` 用户)可读。
- 私钥文件权限建议设置为 `600`。
4.4 证书链完整
推荐使用包含完整中间证书的 `fullchain.pem`(由 Let's Encrypt 或其他 CA 提供),避免某些客户端因缺少中间证书而报错。
4.5 SNI 客户端兼容性
目前几乎所有现代浏览器和操作系统都支持 SNI,只有极少数老旧客户端(如 Windows XP 上的 IE)可能无法访问。如果必须兼容这类客户端,则需要为所有域名使用同一张多域名证书(SAN 证书),或者使用同一个 IP 的不同端口。
4.6 使用多域名证书简化配置
如果你有多个域名需要共用同一个 IP,也可以购买或申请一张包含多个域名的证书(例如 `www.example.com`、`www.anotherexample.com` 都列在证书的 Subject Alternative Names 中)。这样只需一个 `server` 块监听 443,并为所有域名配置同一张证书:
```nginx
server {
listen 443 ssl;
server_name www.example.com www.anotherexample.com; # 多个域名
ssl_certificate /path/to/multi-domain/fullchain.pem;
ssl_certificate_key /path/to/multi-domain/privkey.pem;
...
}
但题目要求的是“单IP多站点SSL证书”,一般理解为每个站点独立证书,因此上述方案更符合原意。
5. 完整配置示例
以下是一个整合了所有要素的 `/etc/nginx/sites-available/example` 文件内容:
nginx
server {
listen 80;
server_name www.example.com example.com;
return 301 https://$host$request_uri;
}
server {
listen 80;
server_name www.anotherexample.com anotherexample.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name www.example.com example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example;
index index.html;
access_log /var/log/nginx/example_access.log;
error_log /var/log/nginx/example_error.log;
可选 HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
}
server {
listen 443 ssl;
server_name www.anotherexample.com anotherexample.com;
ssl_certificate /etc/letsencrypt/live/anotherexample.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/anotherexample.com/privkey.pem;
root /var/www/anotherexample;
index index.html;
access_log /var/log/nginx/anotherexample_access.log;
error_log /var/log/nginx/anotherexample_error.log;
add_header Strict-Transport-Security "max-age=63072000" always;
}
然后在 `/etc/nginx/sites-enabled/` 下创建软链接启用该配置,并执行 `nginx -t` 测试配置,无误后重载 Nginx:
bash
ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx # 或 nginx -s reload
6. 验证配置
可以使用 `openssl` 命令检查 SNI 是否正常工作:
```bash
openssl s_client -connect your_server_ip:443 -servername www.example.com # 应返回 example.com 的证书
openssl s_client -connect your_server_ip:443 -servername www.anotherexample.com # 应返回 anotherexample.com 的证书
如果返回的证书分别对应各自域名,说明配置成功。
通过以上配置,就可以在一个 IP 上托管多个 HTTPS 站点,并为每个站点使用独立的 SSL 证书。