Files
dcalc/docs/image-cors.md
T
2026-11-04 15:35:15 +08:00

4.6 KiB

技能加点图片导出的跨域配置

技能树使用 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 头:

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 和字符集:

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 的环境中执行(容器部署则在对应容器内执行):

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 来源与缓存、Nginx 响应头继承、Nginx set / if。