This commit is contained in:
2026-11-04 15:35:15 +08:00
parent 4709ea4328
commit 62291c57ec
12 changed files with 363 additions and 72 deletions
+87
View File
@@ -0,0 +1,87 @@
# 技能加点图片导出的跨域配置
技能树使用 `https://image.dnftools.com` 上的图标。普通 `<img>` 展示不要求 CORS,但 `html-to-image` 导出时需要 fetch 图标并内嵌到图片,因此图床必须返回允许跨域读取的响应头。前端不需要改动,`no-cors` 无法读取图片内容。
## 允许范围
仅允许 HTTP / HTTPS 的 `*.dnftools.com` 来源,包括多级子域和带端口的来源。例如 `https://dcalc.dnftools.com`、`https://test.dcalc.dnftools.com` 和 `http://dev.dnftools.com:5173`。
不包括裸域 `https://dnftools.com`、`localhost`、`null`、其他域名或 `https://dnftools.com.example.org` 这样的伪装后缀。CORS 响应头不能写成 `*.dnftools.com`,应校验 Origin 后返回该完整 Origin,并设置 `Vary: Origin` 让缓存区分来源。
## Nginx / OpenResty 图床
下面的配置适用于直接编辑站点 `server { ... }` 的环境,不需要在外层 http 中声明 map。
### 1. 在 server 层增加来源校验和默认响应头
放在 `server_name image.dnftools.com;` 后面,保留其余配置和原有的 HSTS 头:
```nginx
set $image_cors_origin "";
if ($http_origin ~* "^https?://([a-z0-9-]+\.)+dnftools\.com(:[0-9]+)?$") {
set $image_cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $image_cors_origin always;
add_header Vary "Origin" always;
```
未匹配的来源对应空值,Nginx 不输出 Access-Control-Allow-Origin。现有 `location /` 没有自己的 add_header,会继承这里的响应头。
### 2. 替换静态资源 location
这个 location 已有自己的 add_header,所以必须在这里也设置 CORS 和 Vary,不能只依赖 server 层。保留原来的文件路径、缓存、HSTS 和字符集:
```nginx
location ~* \.(?:jpg|jpeg|png|gif|webp|avif|svg|ico|bmp|swf|css|js|woff|woff2|ttf|eot)$ {
root /dcalc/image;
expires 7d;
add_header Cache-Control "public, max-age=604800";
add_header Strict-Transport-Security "max-age=31536000";
add_header Access-Control-Allow-Origin $image_cors_origin always;
add_header Vary "Origin" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
add_header Access-Control-Allow-Headers "If-None-Match, If-Modified-Since" always;
add_header Access-Control-Max-Age "86400" always;
if ($request_method = OPTIONS) {
return 204;
}
try_files $uri =404;
charset utf-8,gbk;
}
```
`always` 使 CORS 头也适用于 304 和错误响应;原配置的 `error_page 404 /404.html` 内部跳转会由 `location /` 继承 server 层的 CORS 和 Vary 头。其余 location、证书和 HTTP 跳转配置保持不变。不要再保留之前的 `Access-Control-Allow-Origin "*"`,也不要重复添加此头。
这是对浏览器跨域脚本读取的限制,不是防盗链或访问鉴权;其他来源仍可能展示普通 img,或直接下载公开图片。当前导出无需 Cookie,不需要 Access-Control-Allow-Credentials。
### 3. 检查、重载及验证
在实际运行 Nginx / OpenResty 的环境中执行(容器部署则在对应容器内执行):
```sh
nginx -t
nginx -s reload
# 应返回 Access-Control-Allow-Origin: https://dcalc.dnftools.com
# 并包含 Vary: Origin
curl -I -H 'Origin: https://dcalc.dnftools.com' \
'https://image.dnftools.com/characters/gunner_female/spitfire_female/profile.png'
# 不应返回 Access-Control-Allow-Origin
curl -I -H 'Origin: https://dnftools.com.example.org' \
'https://image.dnftools.com/characters/gunner_female/spitfire_female/profile.png'
```
如果前面还有 CDN,需让其遵守 Vary / 按 Origin 区分缓存,或在边缘执行相同的来源校验,并刷新旧响应头缓存。最后回到技能设置页导出,检查 PNG 中包含图标、等级和觉醒绑定。
## 仓库自带的 Node 图片服务
`pnpm run dev:image` 使用 `scripts/image.js`,已采用同样的来源规则:匹配时返回请求 Origin,其他来源不返回 Access-Control-Allow-Origin;所有响应设置 Vary: Origin,保留 HEAD、304、错误响应和 OPTIONS 支持。
本地前端通过 Vite 的 `/image` 同源代理访问图片,不需要放行 localhost。部署使用 Node 服务时需要重启该进程;当前生产配置直接通过 Nginx 读取 `/dcalc/image`,所以生产生效点是上面的 Nginx 修改,无需重新构建前端。
参考:[CORS 来源与缓存](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin)、[Nginx 响应头继承](https://nginx.org/en/docs/http/ngx_http_headers_module.html)、[Nginx set / if](https://nginx.org/en/docs/http/ngx_http_rewrite_module.html)。