适用场景:一台有公网 IP 的云服务器,不花钱实现 Zotero 全平台(包括 iOS)WebDAV 同步
总体实现思路:服务器部署Alist(或者任意WebDAV),由于iOS端Zotero仅支持有CA证书的https,所以需要nginx反代,然后通过DuckDNS申请免费域名,再通过域名获取Let's Encrypt签发的CA证书
目录
- 前提与概览
- 安装 Docker
- 部署 AList(WebDAV 服务端)
- 获取免费域名(DuckDNS)
- nginx 反向代理 + HTTPS
- Let's Encrypt 证书(acme.sh)
- 自动续签
- Zotero 客户端配置
1. 前提与概览
前提条件
| 项目 | 说明 |
|---|---|
| 云服务器 | 任意云厂商,有公网 IP(本文以 Ubuntu 24.04 为例) |
| SSH 访问 | 能通过 SSH 登录服务器 |
| 域名 | 不需要购买的付费域名,用 DuckDNS 免费方案 |
架构
Zotero 客户端
│
▼ HTTPS (443)
nginx(反向代理 + TLS 终止)
│
▼ HTTP (20000)
AList(WebDAV 服务端)
│
▼ Local storage
/data/(文件存储)
2. 安装 Docker
2.1 安装 Docker
# 更新包索引
sudo apt update && sudo apt upgrade -y
# 安装依赖
sudo apt install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 添加 Docker 仓库
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
# 启动 Docker 并设置开机自启
sudo systemctl enable docker --now
# 将当前用户加入 docker 组(免 sudo 执行 docker)
sudo usermod -aG docker $USER
# 退出重新登录使组生效,或者运行:
newgrp docker
验证:
docker --version应显示版本号。
2.2 安装 Docker Compose(可选但推荐)
sudo apt install -y docker-compose-plugin
docker compose version
3. 部署 AList(WebDAV 服务端)
3.1 创建数据目录
mkdir -p ~/alist-data
3.2 启动 AList 容器
docker run -d \
--restart unless-stopped \
-v ~/alist-data:/opt/alist/data \
-p 20000:5245 \
--name alist \
xhofe/alist:latest
注意:将容器的 5245(AList 默认端口)映射到宿主机的 20000。后续 nginx 会代理 20000。
3.3 一键配置(无需打开浏览器)
AList 初始化后,以下脚本通过 CLI + API 一步完成密码设置、存储挂载和专用用户创建:
# 自行修改下面的变量值
ALIST_ADMIN_PASSWORD="<你的管理员密码>" # 管理员密码
WEBDAV_USERNAME="zotero" # WebDAV 专用用户名
WEBDAV_PASSWORD="<你的WebDAV用户密码>" # WebDAV 专用用户密码
# 1. 设置管理员密码
docker exec alist ./alist admin set "$ALIST_ADMIN_PASSWORD"
# 2. 获取管理员 token(用于 API 鉴权)
ALIST_TOKEN=$(docker exec alist ./alist admin token 2>/dev/null | \
grep "Admin token:" | sed 's/.*Admin token: //')
ALIST_URL="http://localhost:20000"
# 3. 创建存储挂载(/dav → /opt/alist/data)
curl -s -X POST "$ALIST_URL/api/admin/storage/create" \
-H "Content-Type: application/json" \
-H "Authorization: $ALIST_TOKEN" \
-d '{
"mount_path": "/dav",
"driver": "Local",
"order": 0,
"remark": "Zotero WebDAV",
"addition": "{\"root_folder_path\":\"/opt/alist/data\",\"thumbnail\":false,\"show_hidden\":false}"
}'
# 4. 创建 WebDAV 专用用户(role: [0] = 普通用户)
curl -s -X POST "$ALIST_URL/api/admin/user/create" \
-H "Content-Type: application/json" \
-H "Authorization: $ALIST_TOKEN" \
-d "$(cat <<EOF
{
"username": "$WEBDAV_USERNAME",
"password": "$WEBDAV_PASSWORD",
"base_path": "/dav",
"role": [0],
"disabled": false
}
EOF
)"
echo "AList 配置完成!"
echo " WebDAV 地址: http://<你的服务器IP>:20000/dav"
echo " 用户名: $WEBDAV_USERNAME"
echo " 密码: $WEBDAV_PASSWORD"
说明: -
role: [0]表示普通用户,限制其只能访问base_path指定的目录 -base_path: "/dav"限制该用户只能看到 WebDAV 挂载点下的文件 -addition中的root_folder_path对应容器内的物理路径,可根据实际存储位置修改 - 如果alist admin相关命令失败,确保容器已完全启动(docker logs alist查看日志)
3.4 验证 AList 正常工作
# 用 WebDAV 专用用户测试 PROPFIND
curl -s -u "<你的WebDAV用户名>:<你的WebDAV密码>" \
-X PROPFIND -H "Depth: 0" \
http://localhost:20000/dav/ | head -10
返回 XML 格式的目录信息即表示 AList 配置正确。
4. 获取免费域名(DuckDNS)
Let's Encrypt 要求拥有域名才能签发受信任的证书。DuckDNS 提供免费的
yourname.duckdns.org域名。
4.1 注册 DuckDNS
- 打开 duckdns.org
- 用 GitHub / Google / Twitter 账号登录
- 在 Domains 中输入你想要的子域名(如
jhy-zotero),点击 Add domain - 记下显示的 token(用于后续更新 IP)
4.2 解析到服务器
方法 A:手动设置(IP 不变的场景)
DuckDNS 默认将你的子域名解析到你登录时的 IP。如果你的服务器 IP 和登录 DuckDNS 时的 IP 不同,需要:在 DuckDNS 的 current ip 输入框中填写你服务器的公网 IP,点击 Update IP。
方法 B:配置自动更新(IP 可能变动的场景)
# 创建更新脚本
mkdir -p ~/duckdns
cat > ~/duckdns/update.sh << 'EOF'
#!/bin/bash
curl -s "https://www.duckdns.org/update?domains=你的子域名&token=你的token&ip="
EOF
chmod +x ~/duckdns/update.sh
# 添加到 crontab(每 5 分钟更新一次)
(crontab -l 2>/dev/null; echo "*/5 * * * * ~/duckdns/update.sh > ~/duckdns/duck.log 2>&1") | crontab -
4.3 验证域名解析
ping 你的子域名.duckdns.org
# 或
dig +short 你的子域名.duckdns.org
输出应显示你的服务器公网 IP。
5. nginx 反向代理 + HTTPS
5.1 安装 nginx
sudo apt install -y nginx
sudo systemctl enable nginx --now
5.2 配置反向代理
创建 nginx 站点配置:
sudo tee /etc/nginx/sites-enabled/alist << 'EOF'
server {
listen 80;
server_name 你的子域名.duckdns.org;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name 你的子域名.duckdns.org;
# 先使用自签名证书占位,后续会被 Let's Encrypt 替换
ssl_certificate /etc/nginx/ssl/alist.crt;
ssl_certificate_key /etc/nginx/ssl/alist.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:WEBSSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://localhost:20000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
EOF
5.3 生成临时自签名证书(占位用)
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/alist.key \
-out /etc/nginx/ssl/alist.crt \
-subj "/CN=你的子域名.duckdns.org"
5.4 检查并重载 nginx
sudo nginx -t && sudo systemctl reload nginx
此时 HTTPS 已经可用,但使用的是自签名证书(浏览器会报不安全)。下一步用 Let's Encrypt 替换。
6. Let's Encrypt 证书(acme.sh)
6.1 安装 acme.sh
curl https://get.acme.sh | sh
这会安装到 ~/.acme.sh/,并自动添加 cron 任务。
使 acme.sh 命令可用:
source ~/.bashrc
# 或
alias acme.sh=~/.acme.sh/acme.sh
6.2 签发证书
acme.sh 支持多种验证方式。standalone 模式最简单——它会临时监听 80 端口响应 Let's Encrypt 的挑战。
# 确保 80 端口空闲
sudo lsof -i :80 || echo "80 端口空闲"
# 签发证书
~/.acme.sh/acme.sh --issue -d 你的子域名.duckdns.org --standalone --server letsencrypt
签发成功后,证书文件在:~/.acme.sh/你的子域名.duckdns.org_ecc/
6.3 安装证书到 nginx
# 复制证书
sudo cp ~/.acme.sh/你的子域名.duckdns.org_ecc/fullchain.cer /etc/nginx/ssl/alist.crt
sudo cp ~/.acme.sh/你的子域名.duckdns.org_ecc/你的子域名.duckdns.org.key /etc/nginx/ssl/alist.key
# 设置权限
sudo chmod 644 /etc/nginx/ssl/alist.crt
sudo chmod 600 /etc/nginx/ssl/alist.key
# 重载 nginx
sudo nginx -t && sudo systemctl reload nginx
6.4 验证证书
echo | openssl s_client -connect 你的子域名.duckdns.org:443 \
-servername 你的子域名.duckdns.org 2>&1 | \
openssl x509 -noout -subject -issuer -dates
输出应显示:
- subject=CN = 你的子域名.duckdns.org
- issuer=... Let's Encrypt ...
- 有效日期为签发日 + 90 天
7. 自动续签
acme.sh 安装时已自动添加了 cron 任务。验证:
crontab -l | grep acme
输出类似:
58 5,11,17,23 * * * "/home/你的用户名/.acme.sh"/acme.sh --cron --home "/home/你的用户名/.acme.sh" > /dev/null
acme.sh 还支持 ARI(ACME Renewal Information) 功能,会在服务器推荐的续签窗口(通常是到期前 30 天左右)自动续签,无需手动干预。
续签后证书文件内容会更新,acme.sh 会检测到变化。如果证书路径没变,需要手动添加
--reloadcmd "sudo nginx -s reload"到安装命令,或在 cron 中额外执行 nginx 重载。
建议添加证书更新后自动重载 nginx 的钩子:
~/.acme.sh/acme.sh --install-cert -d 你的子域名.duckdns.org \
--reloadcmd "sudo nginx -s reload"
8. Zotero 客户端配置
8.1 Windows / macOS / Linux
Zotero 设置:
1. 打开 Zotero → 编辑 → 首选项 → 同步
2. 文件同步 部分:
- 同步类型:WebDAV
- URL:https://你的子域名.duckdns.org/dav/zotero
- 用户名:AList 中创建的 WebDAV 专用账号
- 密码:对应密码
3. 点击 验证服务器,应显示连接成功
8.2 iOS(iPhone / iPad)
iOS 不需要额外配置证书——Let's Encrypt 是全球受信任的根 CA,iOS 原生信任。
- 在 Zotero App 中进入 设置 → 同步
- 文件同步:
- 类型:WebDAV
- URL:
https://你的子域名.duckdns.org/dav/zotero- 账号密码同上 - 点击验证,应直接通过
8.3 故障排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
Could not connect to server |
防火墙/安全组未放行 443 端口 | 检查云厂商安全组规则 |
SSL_ERROR_BAD_CERT_DOMAIN |
证书域名不匹配 | 确认证书的 CN 包含 duckdns.org 域名 |
| 验证成功但同步失败 | AList WebDAV 目录不存在 | 在 AList 后台创建 /dav/zotero 目录 |
| 浏览器 HTTPS 正常但 Zotero 报错 | Zotero 内置证书存储过期 | 重启 Zotero 或重新导入证书 |