基于 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 rm -f 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.4

    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

🔎 如何正确退出 cat

执行 cat > ... <<'EOF' 之后:

  1. 粘贴或输入上面的内容;
  2. 在新的一行输入 EOF,再按回车
  3. cat自动结束并保存文件。
  4. 或者直接 Ctrl + D退出。

常见误区::wqvim 的用法,不是 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/ssl

openssl 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:latest
    container_name: nginx
    restart: always
    ports:
      - "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

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
EOF

cd /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:latest
    container_name: nginx
    restart: always
    ports:
      - "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 启动正式容器。

posted @ 2025-09-26 11:39  Aminor~  阅读(105)  评论(0)    收藏  举报