https-portal : nginx 全自动申请 SSL 证书解决方案
HTTPS-PORTAL 是一个由 Nginx、Let's Encrypt 和 Docker 驱动的全自动 HTTPS 服务器。使用它,您只需添加一行配置,即可通过 HTTPS 运行任何现有的 Web 应用程序。
SSL 证书是通过 Let's Encrypt 自动获取和更新的。
前言
知道这个可 docker 容器化部署的全自动申请 SSL 证书项目,还是之前安装 outline 的时候,官方给的 docker compose 配置里加的。了解了 https-portal 作用功能之后,感觉到它就是一个宝藏,立马决定作为后续服务器部署项目必备项目了。
回顾搞开发的阶段,一开始是直接服务器裸装 nginx,PHP 等软件的,有时甚至还会搞编译安装。经常会因为安装过程中的某些问题困扰好几天,全网搜索解决方案而抓耳挠腮。后面遇到有同事使用 BT 面板之类的运维面板,可以直接通过 web 页面去安装需要服务器软件了。当时还感觉使用 BT 面板虽然方便,但在服务器上运行自己不掌握的指令脚本(BT 使用 python 开发),总感觉有点别扭。好像之前动则大几个小时的安装折腾突然变成了笑话,努力变成了无用功。
到了现在,接触了 docker 容器化部署之后,突然感觉软件安装进入了另一个新的阶段。只要配置好 docker-compose.yml 文件,执行启动,一个可以相当复杂,依赖 N 多个软件服务支持的项目,可以立马启动起来,软件部署难度下降了好几个 level。
之前 BT 面板就有这样的自动续约 SSL 证书的服务,https-portal 属于 docker 容器化部署的一类,也都是基于 Let's Encrypt 自签证书。
安装 https-portal
首先是创建对应的目录和映射文件夹。ssl-certs 是存放证书的,vhosts 是存放静态网站代码。https-portal 一般是反向代理到本地项目的服务端口,在共享网络内甚至不需要映射出来端口,只需要 expose 就可以通过服务+端口访问。
pwd
/root/https-portal_project
ll
total 28
drwxr-xr-x 4 root root 4096 Aug 4 16:00 ./
drwx------ 25 root root 4096 Aug 4 16:11 ../
-rw-r--r-- 1 root root 1398 Aug 4 16:10 docker-compose.yml
-rw-r--r-- 1 root root 55 Apr 2 14:54 .env
drwxr-xr-x 20 root root 4096 Jun 5 15:56 ssl-certs/
drwxr-xr-x 5 root root 4096 Jul 8 18:51 vhosts/
cat .env
COMPOSE_PROJECT_NAME=https-portal
RENEW_MARGIN_DAYS=30.env 配置了项目名称和证书更新边界天数,就是证书离过期日期的多少天开始自更新证书。
services:
https-portal:
image: steveltn/https-portal:1
container_name: https-portal
env_file: ./.env
ports:
- '80:80'
- '443:443'
networks:
- internal
- shared
restart: always
volumes:
- ./ssl-certs:/var/lib/https-portal
- ./vhosts:/var/www/vhosts
healthcheck:
test: ["CMD", "service", "nginx", "status"]
interval: 30s
timeout: 20s
retries: 3
environment:
DOMAINS: >
xx.seasidecrab.com -> http://xx:12345
STAGE: 'production'
WEBSOCKET: 'true'
CLIENT_MAX_BODY_SIZE: '0'
cap_add:
- SYS_ADMIN
networks:
internal:
driver: bridge
shared:
external: true
name: shared-servicesinternal属于当前的 docker-compose 网络,相当于是局域网,可以访问在当前 docker-compose 定义的服务shared-services是手动创建的共享网络,使 https-portal 可以通过服务名+端口访问到其他服务
https-portal 配置
所有配置在项目 github 上有详细说明:SteveLTN/https-portal - github
域名定义
只要定义了域名,https-portal 启动的时候会检查对应的域名证书目录是否存在,如果不存在会自动申请并配置好。
默认的静态域名站点:DOMAINS: 'example.com'。会在 vhosts 下生成域名目录,然后将静态站点代码放进目录就可以正常访问。
多域名定义
多个域名是在每一个域名定义结尾加一个 ,。
比如 DOMAINS: 'example.com, a.net, b.cn'
但这样如果很多站点的情况下,修改维护会比较麻烦,此时可以使用 YAML 语法支持的多行折叠操作符 >
https-portal:
# ...
environment:
DOMAINS: >
wordpress.example.com -> http://wordpress:80,
gitlab.example.com -> http://gitlab
OTHER_VARS: ...需注意,末尾的 , 需要保留。作者推荐的是 >,测试另一个多行折叠符 | 也是有效的。
几个多行折叠符区别:
|:保留换行|-:保留换行,但去掉最后换行>:折叠换行为空格(末尾会有一个换行符)>-:折叠换行,并删除最后换行
反向代理
域名和服务之间增加一个 ->,表示反向代理。
wordpress.example.com -> http://wordpress:80重定向
域名和服务之间增加一个 =>,表示重定向。
example.com => https://target.example.com多上游源(Upstreams)
域名指向多个服务,服务之间使用 | 分隔。其他参数 [weight=2 max_conns=100] 表示权重和最大连接数。
https-portal:
# ...
environment:
DOMAINS: 'wordpress.example.com -> http://wordpress1:80|wordpress2:80[weight=2 max_conns=100]HTTP 基本认证
认证用户和密码定义在域名前,具体如下:
https-portal:
# ...
environment:
DOMAINS: 'username:password@example.com -> <upstream>'重载 nginx 配置文件
查看 https-portal 日志发现 www 的一些请求报错,核对确定是迁移到 blog 下了,此时需要对 /xx/xx.html 请求跳转到 blog 下。
2026/08/04 10:01:58 [error] 348#348: *555 open() "/var/www/vhosts/www.seasidecrab.com/amp/359.html" failed (2: No such file or directory), client: 223.88.136.245, server: www.seasidecrab.com, request: "GET /amp/359.html HTTP/1.1", host: "www.seasidecrab.com"
2026/08/04 10:02:01 [error] 348#348: *556 open() "/var/www/vhosts/www.seasidecrab.com/Web/747.html" failed (2: No such file or directory), client: 120.227.52.102, server: www.seasidecrab.com, request: "GET /Web/747.html HTTP/1.1", host: "www.seasidecrab.com"相当于要把 *.html 给 301 重定向到 blog 下,www 本身静态站点,保留其他的资源类访问。
正常的 nginx 配置反向代理和 301 重定向:
location ~ \.html$ {
proxy_pass https://blog.seasidecrab.com$request_uri;
proxy_set_header Host b.com;
proxy_set_header X-Real-IP $remote_addr;
}
location ~ \.html$ {
return 301 https://blog.seasidecrab.com$request_uri;
}https-portal 增加 nginx 站点自定义配置:
https-portal:
# ...
volumes:
- ./ssl-certs:/var/lib/https-portal
- ./vhosts:/var/www/vhosts
- ./conf/www.seasidecrab.com.ssl.conf.erb:/var/lib/nginx-conf/www.seasidecrab.com.ssl.conf.erb:rossl 对应的 https 访问,然后按照文档提示修改 erb 模版,添加 html 301 跳转(修复一下报错)。
最终版本 www.seasidecrab.com.ssl.conf.erb:
# This file overrides default nginx HTTPS settings for my.example.com
# Mount this file as "/var/lib/nginx-conf/my.example.com.ssl.conf.erb"
server {
listen 443 ssl;
#listen 443 ssl;
#http2 on;
# domain.name will be "my.example.com", you can also hard-code it.
server_name <%= domain.name %>;
# ssl on;
ssl_certificate <%= domain.chained_cert_path %>;
ssl_certificate_key <%= domain.key_path %>;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache shared:SSL:50m;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
ssl_prefer_server_ciphers on;
ssl_dhparam <%= dhparam_path %>;
location ~* \.html$ {
return 301 https://blog.seasidecrab.com$request_uri;
}
location / {
root /var/www/vhosts/<%= domain.name %>;
index index.html;
try_files $uri $uri/ /index.html;
}
}测试没有报错。
还有很多跟 nginx 相关的配置,比如环境变量设置,或者限制访问 ip 之类的,没怎么用到,这里不做载录。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
海滨擎蟹
微信
支付宝