基于 Docker 的 Nginx + OpenSSL 自签名证书启用 HTTPS(按步骤、可直接复制)
目标:在没有域名的情况下,基于 Docker 部署 Nginx,使用 OpenSSL 生成自签名证书,启用
https://<你的公网IP>
访问。
适用:开发/测试或内部环境(生产请使用域名 + CA 证书)。
0. 前置条件(Prerequisites)
- 一台可用的 Linux 服务器,已安装 Docker(和 docker compose / docker-compose 二选一)。
- 服务器具备公网 IP(文档示例统一用
1.2.3.4
,请替换为你的实际 IP)。 - 宿主机有
openssl
(若没有,文中也提供容器方式)。 - 当前没有名为
nginx
的容器在运行(我们将先暂时创建一个nginx-temp
来导出默认配置,然后删除它)。
1. 准备宿主机目录结构
在宿主机上创建挂载目录:
mkdir -p /data/nginx/{conf,conf.d,log,ssl,static}
目录说明:
/data/nginx/
├── conf/ # 全局 nginx.conf(将从临时容器拷出)
├── conf.d/ # 站点配置(将从临时容器拷出 default.conf,并新增 https-ip.conf)
├── log/ # 日志
├── ssl/ # 证书文件(稍后生成 server.key/server.crt,以及 ip.cnf)
└── static/ # 静态资源(先拷贝默认 index.html,便于验证)
2. 启动临时容器并拷贝默认文件(有容器才能 cp)
这一步只为获取官方默认的配置文件与首页,方便你基于其修改;完成后会删除临时容器。
# 启动临时 nginx 容器(名称:nginx-temp)
docker run -d --name nginx-temp nginx:latest# 从临时容器拷贝默认配置与首页到宿主机
docker cp nginx-temp:/etc/nginx/nginx.conf /data/nginx/conf/nginx.conf
docker cp nginx-temp:/etc/nginx/conf.d/default.conf /data/nginx/conf.d/default.conf
docker cp nginx-temp:/usr/share/nginx/html/index.html /data/nginx/static/index.html# 停止并删除临时容器
docker stop nginx-temp && docker rm nginx-temp
如果你已经对 Nginx 很熟悉,也可以跳过拷贝,直接使用自定义模板。但由于你提出要使用
docker cp
,这里按你的要求保留该流程。
3. 为 HTTPS 新增站点配置(conf.d/https-ip.conf)
我们将新建一个 https-ip.conf
,让 80 端口跳转到 443,并在 443 上启用 SSL。
cat > /data/nginx/conf.d/https-ip.conf <<'EOF'
server {listen 80;server_name _;return 301 https://$host$request_uri;
}server {listen 443 ssl;server_name _; # 或直接写你的公网 IP,例如 1.2.3.4ssl_certificate /etc/nginx/ssl/server.crt;ssl_certificate_key /etc/nginx/ssl/server.key;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers HIGH:!aNULL:!MD5;location / {root /usr/share/nginx/html;index index.html;}
}
EOF
🔎 如何正确退出 cat
执行 cat > ... <<'EOF'
之后:
- 粘贴或输入上面的内容;
- 在新的一行输入
EOF
,再按回车; cat
将自动结束并保存文件。
常见误区:
EOFwq
、:wq
是vim
的用法,不是cat
的用法。
⚠️ 提示:
conf.d/default.conf
中通常也有一个server { listen 80; ... }
,与我们新建的https-ip.conf
会“重复”。若你只希望所有请求都跳转到 HTTPS,建议删除默认文件:rm -f /data/nginx/conf.d/default.conf
4. 生成自签名证书(含 SAN)
现代浏览器要求证书包含 Subject Alternative Name (SAN)。先创建 OpenSSL 配置文件,再生成证书。
4.1 写入 OpenSSL 配置(ip.cnf)
cat > /data/nginx/ssl/ip.cnf <<'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn[dn]
CN = 1.2.3.4[req_ext]
subjectAltName = @alt_names[alt_names]
IP.1 = 1.2.3.4
EOF
退出方法:同上,在新行单独输入 EOF
+ 回车。
请把
1.2.3.4
替换为你的公网 IP。不要在该文件里写中文注释,否则旧版本 OpenSSL 可能解析失败。
4.2 生成证书与私钥(宿主机执行)
cd /data/nginx/sslopenssl req -x509 -nodes -days 365 \-newkey rsa:2048 \-keyout server.key \-out server.crt \-config ip.cnf# 确认文件已生成
ls -l /data/nginx/ssl
# 应包含:server.key server.crt ip.cnf
可选:用容器生成(无 SAN,不推荐)
docker run --rm -v /data/nginx/ssl:/ssl alpine/openssl \sh -c "openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /ssl/server.key -out /ssl/server.crt -subj '/CN=1.2.3.4'"
该方式省事,但通常不含 SAN,某些浏览器会报名称不匹配,建议优先使用上面的
ip.cnf
方案。
5. 编写 docker-compose.yml 并启动正式容器
我们现在有了:配置文件(conf/nginx.conf、conf.d/.conf)以及证书(ssl/)。下一步创建 docker-compose.yml
并启动正式 nginx 容器(容器名:nginx
)。
cat > /data/nginx/docker-compose.yml <<'EOF'
version: '3.9'services:nginx:image: nginx:latestcontainer_name: nginxrestart: alwaysports:- "80:80"- "443:443"volumes:- ./static:/usr/share/nginx/html- ./conf/nginx.conf:/etc/nginx/nginx.conf- ./conf.d:/etc/nginx/conf.d- ./log:/var/log/nginx- ./ssl:/etc/nginx/ssl
EOF
退出方法:同样在新行输入 EOF
+ 回车。
启动服务:
cd /data/nginx
docker compose up -d # 如果是旧版,可用:docker-compose up -d
6. 验证与重载
docker exec -it nginx nginx -t
docker exec -it nginx nginx -s reload
如需查看日志:
docker logs nginx
7. 测试访问
浏览器打开:
https://1.2.3.4
第一次会出现 “证书不受信任” 的提示(因为是自签名证书),继续访问即可看到默认首页。
若你希望消除浏览器警告,可把
/data/nginx/ssl/server.crt
导入到你电脑系统/浏览器的受信任根证书中(不同系统导入方法略有差异)。
8. 常见问题(FAQ)
Q1:cannot load certificate "/etc/nginx/ssl/server.crt"
A:检查 compose 是否挂载了证书目录(./ssl:/etc/nginx/ssl
),并确认 /data/nginx/ssl/server.crt
存在。进入容器查看:
docker exec -it nginx ls -l /etc/nginx/ssl
Q2:ip.cnf
报错 missing equal sign
A:确认每行均为 key = value
格式;不要写中文注释;严格按示例粘贴。
Q3:Here Document 结束写错
A:结束行必须单独写 EOF
;不要写 EOFwq
或 :wq
。
Q4:80 端口没有跳转到 443
A:确认 https-ip.conf
中包含 80 跳转 server;并避免与 default.conf
冲突(必要时删除 default.conf
)。
Q5:我已经有自己的首页
A:直接把你的网站文件放到 /data/nginx/static/
即可覆盖默认 index.html
。
9. 命令速查(一屏总结)
# 目录
mkdir -p /data/nginx/{conf,conf.d,log,ssl,static}# 临时容器 -> 拷贝默认配置与首页 -> 清理
docker run -d --name nginx-temp nginx:latest
docker cp nginx-temp:/etc/nginx/nginx.conf /data/nginx/conf/nginx.conf
docker cp nginx-temp:/etc/nginx/conf.d/default.conf /data/nginx/conf.d/default.conf
docker cp nginx-temp:/usr/share/nginx/html/index.html /data/nginx/static/index.html
docker stop nginx-temp && docker rm nginx-temp
rm -f /data/nginx/conf.d/default.conf # 如只需 HTTPS 跳转,建议删掉默认站点# 新建 HTTPS 站点配置(80 跳 443)
cat > /data/nginx/conf.d/https-ip.conf <<'EOF'
server {listen 80;server_name _;return 301 https://$host$request_uri;
}
server {listen 443 ssl;server_name _;ssl_certificate /etc/nginx/ssl/server.crt;ssl_certificate_key /etc/nginx/ssl/server.key;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers HIGH:!aNULL:!MD5;location / { root /usr/share/nginx/html; index index.html; }
}
EOF# 生成证书(含 SAN)
cat > /data/nginx/ssl/ip.cnf <<'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[dn]
CN = 1.2.3.4
[req_ext]
subjectAltName = @alt_names
[alt_names]
IP.1 = 1.2.3.4
EOFcd /data/nginx/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout server.key -out server.crt -config ip.cnf# docker-compose.yml
cat > /data/nginx/docker-compose.yml <<'EOF'
version: '3.9'
services:nginx:image: nginx:latestcontainer_name: nginxrestart: alwaysports:- "80:80"- "443:443"volumes:- ./static:/usr/share/nginx/html- ./conf/nginx.conf:/etc/nginx/nginx.conf- ./conf.d:/etc/nginx/conf.d- ./log:/var/log/nginx- ./ssl:/etc/nginx/ssl
EOF# 启动与验证
cd /data/nginx
docker compose up -d
docker exec -it nginx nginx -t
docker exec -it nginx nginx -s reload
echo "Open https://1.2.3.4 in your browser"
完成! 以上步骤保证顺序清晰:先有临时容器用于 docker cp
,再写 HTTPS 站点与证书,最后用 compose 启动正式容器。祝你部署顺利!