在本地或测试服务器上开发 Web 应用或 API 时,通常需要 HTTPS 来测试 Service Workers、地理位置 API、剪贴板 API、WebCrypto 或混合内容策略等功能。在这个阶段获取真实的 SSL 证书太过分了 — 用 OpenSSL 生成的自签证书是最快、最免费的替代方案。本指南逐步讲解:安装 OpenSSL、理解证书文件类型、生成带主体备用名称 (SAN) 的密钥和证书、配置 Apache 和 Nginx,以及将证书添加到系统信任存储,使浏览器警告完全消失。
什么是自签 SSL 证书,何时应使用它?
自签 SSL 证书是你使用 OpenSSL 自己生成和签署的 X.509 证书,而不是向 DigiCert、Sectigo 或 Let's Encrypt 等受信任的证书颁发机构 (CA) 提交证书签名请求 (CSR)。因为浏览器不识别签发机构,它们会显示"你的连接不安全"或 NET::ERR_CERT_AUTHORITY_INVALID 等安全警告。
尽管有此限制,自签证书在以下几种情况下是正确的工具:
- 本地开发 — 在 localhost 上测试仅 HTTPS 浏览器功能(Service Workers、摄像头访问、WebAuthn、剪贴板 API、HTTP/2),无需部署到真实服务器。
- 测试和 UAT 环境 — 仅由团队成员访问的内部服务器,你可以集中管理信任策略。
- 内部工具 — API 网关、仪表板或管理面板,仅在私有网络或 VPN 中访问。
- 测试 TLS 配置 — 在服务器上线前验证 TLS 协议版本、密码套件和 HSTS 标头。
- 容器网络 — Docker 或 Kubernetes 网络中微服务之间的相互 TLS (mTLS),所有参与者都受信任。
重要:切勿在面向公众的生产服务器上使用自签证书。所有主要浏览器都会在访问者继续前用整页警告阻止他们,严重损害信任和转化率。
了解证书文件类型
生成证书前,了解每种文件类型的用途会很有帮助:
| 文件 / 扩展名 | 说明 | 使用者 |
|---|---|---|
server.key |
私钥 — 保持机密,永不分享 | Apache / Nginx Web 服务器 |
server.csr |
证书签名请求 — 发送给 CA(或自签) | CA 提交或自签 |
server.crt |
PEM 格式的证书 — 公开证书 | Web 服务器 + 信任存储导入 |
server.pem |
PEM 格式 — 与 .crt 相同,某些工具需要此扩展名 | Linux CA 存储、Docker、Node.js |
server.pfx / .p12 |
PKCS#12 — 在单个二进制文件中捆绑密钥 + 证书 | Windows IIS、.NET、Java KeyStore |
对于运行 Apache 或 Nginx 的基于 Linux 的 Web 服务器,你将主要使用 PEM 格式的 .key(私钥)和 .crt(证书)文件 — 这是以 -----BEGIN CERTIFICATE----- 开头的 Base64 编码文本格式。
验证 OpenSSL 并准备环境
OpenSSL 在大多数 Linux 发行版和 macOS 上都已预装。Windows 用户必须单独安装。用以下命令确认可用性:
openssl version
预期输出:
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
如果未安装 OpenSSL,使用相应的包管理器:
# Ubuntu / Debian sudo apt-get update && sudo apt-get install -y openssl # CentOS / RHEL / Rocky Linux sudo dnf install -y openssl # macOS 通过 Homebrew brew install openssl # Windows:从 https://slproweb.com/products/Win32OpenSSL.html 下载 Win32/Win64 OpenSSL # 或使用 WSL (Windows Subsystem for Linux) 直接运行 Linux 命令。
为证书文件创建工作目录:
mkdir -p ~/ssl-certs && cd ~/ssl-certs
方法 1 — 快速一行命令证书
最快的方法在单一命令中结合密钥生成和证书创建。这适合快速开发环境设置:
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \ -days 365 -nodes \ -subj "/C=TH/ST=Bangkok/L=Bangkok/O=MyCompany Dev/OU=Development/CN=localhost"
参数说明:
req -x509— 直接创建自签证书,绕过 CA-newkey rsa:4096— 生成新的 4096 位 RSA 私钥-keyout server.key— 将私钥写入 server.key-out server.crt— 将证书写入 server.crt-days 365— 证书有效期为 365 天-nodes— 不加密私钥("no DES"),使 Web 服务器可以在不提示密码的情况下启动-subj— 非交互式提供主体详情
运行后,你将获得两个文件:server.key(私钥)和 server.crt(证书),两者都可在 Web 服务器中配置。
方法 2 — 带主体备用名称 (SAN) 的证书
现代浏览器要求证书包含主体备用名称 (SAN),否则会显示额外警告 — "缺少主体备用名称" — 即使你已接受证书。以下方法使用配置文件创建覆盖多个主机名和 IP 地址的证书:
# Create SAN configuration file cat > san.cnf << 'EOF' [req] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn x509_extensions = v3_req [dn] C = TH ST = Bangkok L = Bangkok O = 我的公司开发 CN = localhost [v3_req] subjectAltName = @alt_names keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth [alt_names] DNS.1 = localhost DNS.2 = *.localhost DNS.3 = dev.local DNS.4 = *.dev.local DNS.5 = staging.myapp.com IP.1 = 127.0.0.1 IP.2 = 192.168.1.100 EOF # 从配置生成证书 openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \ -days 365 -nodes -config san.cnf
这张单一证书覆盖 localhost、*.localhost、dev.local、*.dev.local、staging.myapp.com 以及指定的 IP 地址 — 无需为开发环境中的每个主机名生成单独的证书。
方法 3 — 分步证书生成
为了获得更好的控制或需要保留 CSR 以提交给内部 CA 时,将流程分为三个明确步骤:
第 1 步:生成私钥
# RSA 4096 位(推荐) openssl genrsa -out server.key 4096 # 备选:ECDSA P-256(密钥生成和 TLS 握手更快) openssl ecparam -genkey -name prime256v1 -out server.key
第 2 步:创建 CSR
openssl req -new -key server.key -out server.csr \ -subj "/C=TH/ST=Bangkok/L=Bangkok/O=MyOrg/CN=localhost"
第 3 步:自签 CSR
openssl x509 -req -in server.csr -signkey server.key \ -out server.crt -days 365 \ -extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1")
如果你稍后决定由组织的内部 CA 签署而不是自签,保留 CSR (server.csr) 很有用。
验证证书
在部署生成的证书前务必检查它以确认详情正确:
# 查看完整证书详情 openssl x509 -in server.crt -text -noout # 仅查看主体、颁发者和有效期日期 openssl x509 -in server.crt -subject -issuer -dates -noout # 确认私钥与证书匹配(MD5 哈希必须相同) openssl x509 -noout -modulus -in server.crt | md5sum openssl rsa -noout -modulus -in server.key | md5sum # 确认 SAN 条目 openssl x509 -in server.crt -text -noout | grep -A2 "Subject Alternative Name"
输出中要验证的关键字段:
- 有效期 — 正确的生效日期和过期日期
- 主体 CN — 与你的目标主机名匹配
- X509v3 主体备用名称 — 列出了所有所需的 DNS 名称和 IP
- 公钥算法 — RSA 4096 位或 EC prime256v1
配置 Web 服务器
将证书文件复制到标准系统位置,然后配置 Web 服务器使用它们。下面的示例假设证书存储在 /etc/ssl/self-signed/:
# 复制证书文件 sudo mkdir -p /etc/ssl/self-signed sudo cp server.key /etc/ssl/self-signed/server.key sudo cp server.crt /etc/ssl/self-signed/server.crt sudo chmod 600 /etc/ssl/self-signed/server.key sudo chmod 644 /etc/ssl/self-signed/server.crt
Nginx 配置
server {
listen 80;
server_name localhost dev.local;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name localhost dev.local;
ssl_certificate /etc/ssl/self-signed/server.crt;
ssl_certificate_key /etc/ssl/self-signed/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root /var/www/html;
index index.html index.php;
location / {
try_files $uri $uri/ =404;
}
}
Apache 配置
<VirtualHost *:443>
ServerName localhost
ServerAlias dev.local
SSLEngine on
SSLCertificateFile /etc/ssl/self-signed/server.crt
SSLCertificateKeyFile /etc/ssl/self-signed/server.key
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName localhost
Redirect permanent / https://localhost/
</VirtualHost>
进行更改后测试并重启 Web 服务器:
# Nginx sudo nginx -t && sudo systemctl reload nginx # Apache sudo apachectl configtest && sudo systemctl reload apache2
将证书添加到系统信任存储
要在开发环境中完全消除浏览器警告,将证书导入操作系统的信任存储。这必须在每个开发人员机器上单独进行。
macOS
# 通过命令行 sudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain server.crt # 通过钥匙串访问 GUI: # 1. 双击 server.crt 打开钥匙串访问 # 2. 找到证书条目 — 双击它 # 3. 展开信任 → "使用此证书时" → 始终信任
Linux — Ubuntu / Debian
sudo cp server.crt /usr/local/share/ca-certificates/dev-localhost.crt sudo update-ca-certificates # 输出:1 added, 0 removed; done.
Windows
# PowerShell(以管理员身份运行) Import-Certificate -FilePath "C:\path\to\server.crt" ` -CertStoreLocation Cert:\LocalMachine\Root # 或通过 GUI: # 1. 双击 server.crt → 安装证书 # 2. 存储位置:本地计算机 → 下一步 # 3. 将所有证书放入:受信任的根证书颁发机构 → 确定 → 完成
专业提示 — 面向团队的 mkcert:如果你的团队频繁生成自签证书,考虑 mkcert (github.com/FiloSottile/mkcert)。它自动创建本地 CA 并用单一命令将其安装到每个操作系统的系统信任存储中:mkcert -install。然后用 mkcert localhost 127.0.0.1 ::1 生成受信任证书 — 无需 OpenSSL 配置文件。一次分发 CA 证书 (rootCA.pem) 给所有团队成员,随后由 mkcert 生成的每个证书都将自动被信任。
在 Docker 和 Node.js 中使用自签 SSL
在容器化开发环境中经常需要自签证书。以下是最常见的模式:
Node.js HTTPS 服务器
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt'),
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Hello HTTPS\n');
}).listen(443, () => console.log('HTTPS server running on :443'));
Docker Compose 搭配 Nginx + SSL
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "443:443"
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./ssl/server.crt:/etc/ssl/certs/server.crt:ro
- ./ssl/server.key:/etc/ssl/private/server.key:ro
- ./html:/usr/share/nginx/html:ro
在开发工具中禁用 SSL 验证
某些工具默认拒绝自签证书。这些标志仅在开发中使用 — 在生产环境中始终重新启用证书验证:
# curl
curl -k https://localhost/api/health
# wget
wget --no-check-certificate https://localhost/file.zip
# Python requests
import requests
response = requests.get('https://localhost', verify=False)
# Node.js 环境变量
export NODE_TLS_REJECT_UNAUTHORIZED=0
# Git(按存储库设置,不是全局)
git -c http.sslVerify=false clone https://localhost/repo.git
为团队创建私有证书颁发机构
如果你的团队频繁使用内部 HTTPS 服务,更好的长期解决方案是创建私有根 CA,一次分发其证书给每个团队成员,然后用它签署所有服务器证书。新证书将自动被信任,无需任何额外导入。
# 第 1 步:创建根 CA 私钥 openssl genrsa -out ca.key 4096 # 第 2 步:创建根 CA 证书(10 年有效期) openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -out ca.crt \ -subj "/C=TH/ST=Bangkok/O=MyCompany Dev CA/CN=MyCompany Development Root CA" # 第 3 步:创建服务器私钥 openssl genrsa -out server.key 4096 # 第 4 步:创建服务器 CSR openssl req -new -key server.key -out server.csr \ -subj "/C=TH/ST=Bangkok/O=MyCompany/CN=dev.mycompany.local" # 第 5 步:用 CA 签署服务器 CSR openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 365 -sha256 \ -extfile <(printf "subjectAltName=DNS:dev.mycompany.local,DNS:*.dev.mycompany.local,IP:192.168.1.100")
分发 ca.crt 给每个团队成员一次安装在其信任存储中。随后由此 CA 签署的所有服务器证书都将自动被信任 — 无需额外的逐个证书导入。
续期自签证书
与 CA 签发的证书不同,自签证书没有续期流程 — 当旧证书过期或主体信息更改时,你只需生成新证书。检查过期日期:
# 检查过期日期 openssl x509 -in server.crt -noout -enddate # notAfter=Jun 9 00:00:00 2027 GMT # 使用保存的配置文件重新生成 openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \ -days 365 -nodes -config san.cnf # 续期后重启 Web 服务器 sudo systemctl reload nginx
对于自动化环境,添加此命令到 cron 作业,在证书过期前 30 天运行,之后重启 Web 服务器。
常见问题
自签 SSL 与 CA 签发证书的区别是什么?
自签 SSL 证书是由你使用 OpenSSL 生成和签署的 — 不涉及证书颁发机构。浏览器不识别签发机构,所以会显示"你的连接不安全"的警告。CA 签发的证书(来自 DigiCert、Let's Encrypt 等)受全球浏览器信任。自签证书仅应用于开发、测试或内部工具中,且你能控制谁可以访问服务。
我能在生产服务器上使用自签 SSL 吗?
强烈不建议。所有主要浏览器 — Chrome、Firefox、Safari 和 Edge — 都会在访问者到达你的网站前显示整页的"不安全"警告。这会严重破坏用户信任和转化率。API 客户端也默认拒绝自签证书,除非你明确禁用验证。对于生产环境,使用来自受信任 CA 的证书或免费的 Let's Encrypt 服务。
我如何让浏览器信任自签 SSL 证书?
将 .crt 文件导入操作系统的信任存储。在 macOS 上,使用钥匙串访问或 sudo security add-trusted-cert。在 Windows 上,使用 certmgr.msc → 受信任的根证书颁发机构 → 导入。在 Linux (Ubuntu/Debian) 上,复制到 /usr/local/share/ca-certificates/ 并运行 sudo update-ca-certificates。这仅适用于你控制的计算机 — 无法让外部用户信任证书。
自签 SSL 证书的有效期有多长?
你可以用 -days 参数自己设置有效期。常见值是 365(一年)或 3650(十年)用于开发环境。但现代浏览器 — 尤其是 iOS 和 macOS 上的 Safari — 强制要求证书有效期最长约 825 天。有效期更长的证书可能触发额外安全警告。最佳实践是每年重新生成证书,或在主体信息更改时重新生成。