
适用版本:Gitea 1.20 / 1.21 / 1.22(配置文件键名以官方文档为准) 目标读者:负责 Gitea 部署与运维的工程师 关键词:Gitea、CDN、对象存储、MinIO、SERVE_DIRECT、STATIC_URL_PREFIX
在默认(非 CDN)部署下,Gitea 的所有响应都由源站(Gitea + 其反向代理)直接处理。实际运行中会碰到以下问题:
git clone 会触发归档打包与下载,单次克隆动辄几十上百 MB,高峰期直接打满源站出口带宽。核心结论:CDN 化的本质是"把大文件与静态资源从源站剥离出去",让 Gitea 专注处理元数据与业务逻辑。
资源类型 | 说明 | 是否适合 CDN |
|---|---|---|
公共前端资源(JS/CSS/字体/图片) | /assets/ 下的构建产物 | ✅ 适合,可长时间缓存 |
用户头像 | 本地头像或 Gravatar 代理 | ✅ 适合 |
附件(Issue/PR 上传、Release 附件) | /attachments/ 下的对象 | ⚠️ 私有仓库附件需鉴权 |
仓库归档(zip/tar.gz) | clone/download 时动态或预生成 | ⚠️ 私有仓库归档需鉴权 |
LFS 对象 | 大文件存储 | ⚠️ 私有仓库需签名直链 |
Gitea 自身不是 CDN,也不内置 CDN 能力。它通过两条路径实现 CDN 化:
/assets/、头像等静态资源的 URL 前缀改写为 CDN 域名,由 CDN 直接返回。两者可独立或组合使用。
配置项 | 改造前(非 CDN) | 改造后(CDN 模式) | 说明 |
|---|---|---|---|
[server] ROOT_URL | https://git.example.com/ | https://git.example.com/(不变) | 必须保持为 Gitea 自身公开地址 |
[server] STATIC_URL_PREFIX | 空 | https://cdn.example.com | 静态资源前缀指向 CDN |
[server] OFFLINE_MODE | false | false(或按需 true) | 离线模式,见下 |
[storage] STORAGE_TYPE | local | minio / s3 | 存储后端 |
[storage] SERVE_DIRECT | false | true | 重定向到对象存储签名直链 |
[picture] GRAVATAR_SOURCE | gravatar | 自建/镜像源或disable | 头像 CDN 化 |
[picture] DISABLE_GRAVATAR | false | 按需true | 禁用外部 Gravatar |
[server] ROOT_URL ——【重要】ROOT_URL 必须保持为 Gitea 实例自身的公开地址(如 https://git.example.com/)。
⚠️ 绝不能把
ROOT_URL指向 CDN 域名。
原因:ROOT_URL 决定:
git clone 时输出的克隆地址。⚠️ OAuth 回调地址必须使用 HTTPS,且必须指向 ROOT_URL(Gitea 自身),绝不能指向 CDN,否则存在 OAuth 劫持风险。
ROOT_URL 一致。[server] STATIC_URL_PREFIX将静态资源指到 CDN 域名,例如 https://cdn.example.com。
旧版配置项
ASSETS_URL_PREFIX已被STATIC_URL_PREFIX取代,新部署请使用后者。
[server] OFFLINE_MODEfalse:允许加载外部资源(如外部 Gravatar、CDN 字体)。true:禁止访问外部网络资源,适合内网隔离环境。CDN 化后若 CDN 属自建可控,按需保留 false。
[storage] STORAGE_TYPE取值:local(默认)、minio、s3。
改为对象存储后,附件、LFS、归档等大文件不再占用本地磁盘,并且可以被 CDN 直接回源。
[storage] SERVE_DIRECT ——【关键】SERVE_DIRECT = true 时,Gitea 对附件、LFS、仓库归档不再自己转发字节流,而是重定向(HTTP 302)到对象存储生成的带签名临时直链。
这是真正卸载源站流量的关键开关:用户的下载流量直接从对象存储/CDN 走,源站只返回一个 302。
[storage.minio] 真实键名键名 | 说明 |
|---|---|
MINIO_ENDPOINT | 对象存储访问地址,如minio.example.com:9000 |
MINIO_ACCESS_KEY_ID | Access Key |
MINIO_SECRET_ACCESS_KEY | Secret Key |
MINIO_BUCKET | 存储桶名称,如gitea |
MINIO_USE_SSL | 是否使用 SSL,true / false |
MINIO_BASE_PATH | 桶内基础路径前缀 |
MINIO_LOCATION | 区域(region),如us-east-1 |
MINIO_CHECKSUM_ALGORITHM | 校验算法,如md5 / sha256 |
[picture] 头像 CDN 化GRAVATAR_SOURCE:头像来源,可设为自建镜像或 disable。DISABLE_GRAVATAR:true 时禁用外部 Gravatar,避免请求外部服务带来的跨境延迟。思路:保留源站与本地存储,Nginx 按路径分离静态资源,CDN 回源 Gitea/Nginx。
Nginx 路径分离示例:
server {
listen 443 ssl;
server_name git.example.com;
# 公共前端资源:长缓存
location /assets/ {
proxy_pass http://127.0.0.1:3000;
expires 30d;
add_header Cache-Control "public, immutable";
}
# 头像:中等缓存
location /avatars/ {
proxy_pass http://127.0.0.1:3000;
expires 1d;
add_header Cache-Control "public";
}
# 安全响应头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
# 附件:需鉴权,禁止公共缓存
location /attachments/ {
proxy_pass http://127.0.0.1:3000;
add_header Cache-Control "private, no-store";
}
location / {
proxy_pass http://127.0.0.1:3000;
}
}CDN 侧:把 /assets/、/avatars/ 配置为缓存回源,/attachments/、归档路径设为不缓存或透传。
适用场景:不想引入对象存储、只想先卸载静态资源与加速前端的小型部署。
局限:大文件(归档/LFS)仍从源站出口,带宽痛点未根本解决。
思路:以 MinIO/S3/OSS 作为 STORAGE_TYPE,开启 SERVE_DIRECT = true,CDN 回源对象存储。
[storage]
STORAGE_TYPE = minio
SERVE_DIRECT = true
[storage.minio]
MINIO_ENDPOINT = minio.example.com:9443
MINIO_ACCESS_KEY_ID = your-access-key
MINIO_SECRET_ACCESS_KEY = your-secret-key
MINIO_BUCKET = gitea
MINIO_USE_SSL = true
MINIO_BASE_PATH = gitea/
MINIO_LOCATION = us-east-1
MINIO_CHECKSUM_ALGORITHM = md5⚠️ 示例仅用于演示,生产环境使用占位符并通过环境变量或密钥管理服务提供真实凭证。
MINIO_ACCESS_KEY_ID和MINIO_SECRET_ACCESS_KEY严禁硬编码在app.ini中。TLS 版本要求:MinIO / CDN / Nginx 全链路应至少启用 TLSv1.2(禁用 TLSv1.0/TLSv1.1),端口使用 9443(HTTPS)。
生产环境密钥管理建议:
EnvironmentFile 或启动脚本注入环境变量app.ini 不支持 ${VAR} 插值语法MinIO Bucket 安全策略:
数据流:用户请求归档 → Gitea 返回 302 到对象存储签名直链 → 用户流量走 CDN/对象存储,源站仅处理元数据。
维度 | 方案 A(纯 CDN 回源) | 方案 B(对象存储 + CDN) |
|---|---|---|
大文件流量 | 仍走源站 | 完全卸载 |
部署复杂度 | 低 | 中(需对象存储) |
扩展性 | 一般 | 高 |
安全边界 | 依赖 Nginx/CDN 规则 | 依赖签名直链 |
推荐度 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
推荐方案 B:只有把大文件迁到对象存储并开启
SERVE_DIRECT,才能从根本上卸载源站带宽。
# 备份配置
cp /etc/gitea/app.ini /etc/gitea/app.ini.bak.$(date +%F)
# 备份 PostgreSQL
pg_dumpall -U postgres > /backup/gitea_pg_$(date +%F).sql
# 备份数据目录(含本地存储、LFS)
tar czf /backup/gitea_data_$(date +%F).tar.gz /var/lib/giteacdn.example.com)并完成 ICP/证书配置。git.example.com;方案 B 回源对象存储。/assets/ 长缓存,/avatars/ 中缓存,附件/归档按鉴权策略。改造前:
[server]
ROOT_URL = https://git.example.com/
OFFLINE_MODE = false
[storage]
STORAGE_TYPE = local
SERVE_DIRECT = false
[picture]
GRAVATAR_SOURCE = gravatar
DISABLE_GRAVATAR = false改造后:
[server]
ROOT_URL = https://git.example.com/
STATIC_URL_PREFIX = https://cdn.example.com
OFFLINE_MODE = false
[storage]
STORAGE_TYPE = minio
SERVE_DIRECT = true
[storage.minio]
MINIO_ENDPOINT = minio.example.com:9443
MINIO_ACCESS_KEY_ID = your-access-key
MINIO_SECRET_ACCESS_KEY = your-secret-key
MINIO_BUCKET = gitea
MINIO_USE_SSL = true
MINIO_BASE_PATH = gitea/
MINIO_LOCATION = us-east-1
MINIO_CHECKSUM_ALGORITHM = md5
> ⚠️ **示例仅用于演示,生产环境使用占位符并通过环境变量或密钥管理服务提供真实凭证**。`MINIO_ACCESS_KEY_ID` 和 `MINIO_SECRET_ACCESS_KEY` 严禁硬编码在 `app.ini` 中。
> **生产环境密钥管理建议**:
> - 使用 systemd 的 `EnvironmentFile` 或启动脚本注入环境变量
> - 或使用 HashiCorp Vault、AWS Secrets Manager 等密钥管理服务
> - Gitea `app.ini` 不支持 `${VAR}` 插值语法
[picture]
GRAVATAR_SOURCE = disable
DISABLE_GRAVATAR = true若从
local切换到对象存储,需先迁移历史数据:可使用gitea dump导出后再导入,或通过mc mirror将本地存储目录同步到桶内对应路径。
sudo systemctl restart gitea
sudo systemctl status gitea# 检查静态资源是否命中 CDN
curl -I https://cdn.example.com/assets/index.js
# 检查附件/归档是否 302 到对象存储
curl -I https://git.example.com/user/repo/archive/main.zip观察响应头:
cf-cache-status: HIT(Cloudflare)或 x-cache: HIT(Nginx/常见 CDN)表示命中缓存。Location: 指向对象存储直链(含签名)表示 SERVE_DIRECT 生效。在 CDN 控制台查看 Cache Hit Ratio,稳态下静态资源应 > 90%。
对比改造前后源站出口带宽与 QPS 曲线,归档/附件下载应显著下降。
打开 Network 面板,检查静态资源 Remote Address 是否指向 CDN、归档请求是否 302。
CDN 缓存不更新:前端发版后需在 CDN 控制台主动 purge(刷新)对应目录;/assets/ 建议用带哈希文件名的不可变资源,避免刷新。
ROOT_URL 配错导致 404/跳转异常:检查 ROOT_URL 是否被误指向 CDN,务必改回实例地址。
CORS 跨域:CDN 域名与站点域名不同源,需在 CDN 配置 Access-Control-Allow-Origin 等响应头。
Access-Control-Allow-Origin 必须设置为 Gitea 域名(如 https://git.example.com),不能使用 * 通配符私有仓库归档与 LFS 的安全边界:绝不可对私有仓库的归档/LFS 做公共缓存。SERVE_DIRECT 通过带签名的临时直链访问对象存储,签名短时效,避免越权;CDN 侧对这类路径应设为不缓存或仅缓存鉴权后的响应。
签名直链算法:Gitea 对象存储直链采用 HMAC-SHA256 签名,包含时间戳、过期时间(建议 5-15 分钟)、请求 ID(防重放)。签名计算方式为:
signature = HMAC-SHA256(secret_key, timestamp + request_id + path)有效期与防重放:签名有效期为 5-15 分钟,服务端校验时间戳与 nonce(请求 ID),拒绝过期或重复请求。
HTTPS 混合内容:若站点为 HTTPS 而 CDN 为 HTTP,浏览器会拦截资源,必须为 CDN 配置有效证书。
改造失败时可快速回滚:
# 1. 停止服务
sudo systemctl stop gitea
# 2. 还原 app.ini
cp /etc/gitea/app.ini.bak.2026-09-26 /etc/gitea/app.ini
# 3. 如涉及对象存储数据,按需还原数据目录
# tar xzf /backup/gitea_data_2026-09-26.tar.gz -C /
# 4. 重启
sudo systemctl start gitea回滚前建议再次备份当前配置,确保可双向切换。
CDN 化改造的要点可归纳为三条:
ROOT_URL 指向 Gitea 自身,STATIC_URL_PREFIX 指向 CDN —— 分清"谁是站点"和"谁是分发"。SERVE_DIRECT = true,用签名直链卸载源站流量。遵循以上原则,配合备份与回滚预案,即可平滑完成从非 CDN 到 CDN 模式的迁移。