站长高频疑问:CORS、混合内容、缓存、WMode
问题一:SWF 加载跨域失败(CORS)
症状:Console 里看到红色报错 Access to fetch ... has been blocked by CORS policy 或 CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource。
原因:Ruffle 用 fetch() 加载 SWF,浏览器要求服务器返回 Access-Control-Allow-Origin 头。
三种解法:
解法 A:SWF 放在自己同一个源的服务器上(推荐)
最简单——SWF 和页面同域同源,不存在 CORS 问题。如果你的老 Flash 游戏本来就放在自己站点里,这条就够用。
解法 B:给 SWF 所在服务器加 CORS 头
如果 SWF 必须从别的服务器加载(比如 CDN),在那个服务器上加:
Access-Control-Allow-Origin: *
Nginx 示例:
location ~* \.swf$ {
add_header Access-Control-Allow-Origin *;
}
Apache:
<FilesMatch "\.swf$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
Cloudflare 有免费的 Transform Rules 可以加响应头。
解法 C:本地跨域代理(临时调试用)
如果 SWF 在别人服务器上、且你改不了它的 CORS 头——本地起个简单代理把请求转发一下。上线前还是要回到解法 A 或 B。
问题二:混合内容(Mixed Content)
症状:页面是 HTTPS,但 SWF 或 SWF 内部请求的资源是 HTTP → Console 里 Mixed Content: ... was loaded over HTTPS, but requested an insecure ... This request has been blocked。
解法:Ruffle 默认 upgradeToHttps: true 会自动把 HTTP 升级成 HTTPS。确保配置里这个选项是开的。如果升级后目标地址不存在,就得手动修 SWF 里写死的 HTTP URL(改 SWF 内容)。
问题三:CDN / 浏览器缓存了旧版 Ruffle
症状:你更新了 selfhosted 的 ruffle.js,但访客还在用老版本。
解法:
- CDN 缓存:unpkg 默认无缓存,jsDelivr 可能滞后几小时。用 Cloudflare 等自建 CDN 的话,部署后 Purge 一下边缘缓存
- 浏览器缓存:最简单的办法是改路径名带版本号,比如
/js/ruffle/v0.7.1/ruffle.js升级后改成/js/ruffle/v0.7.2/ruffle.js——浏览器看到新 URL 就重新下载 - 或者加 cache-bust 参数:
/js/ruffle/ruffle.js?v=20261012
问题四:Flash 透明背景(WMode=transparent)
症状:原 Flash Player 有 wmode="transparent" 参数让 Flash 画布显示网页底下的背景色 / 图片,但 Ruffle 替换后变成不透明黑底。
解法:在创建播放器时或 polyfill 配置里明确设背景色:
// 方式 1:全局覆盖
window.RufflePlayer.config = {
backgroundColor: null // null 表示透明
};
或者:
// 方式 2:CSS 强制让 Ruffle 容器透明
#ruffle-player-xxx {
background: transparent !important;
}
#ruffle-player-xxx canvas {
background: transparent !important;
}
注意:Ruffle 的透明支持在 Web 版上不如旧 Flash Player 完美(旧 Flash 用的是浏览器原生插件的特殊合成模式),Ruffle 用 Canvas + WebGL 模拟。如果你的透明层里还有 HTML 覆盖(半透明导航条、浮动广告),可能会有叠层顺序问题——这是浏览器安全模型的限制,Ruffle 层面没法彻底绕过。
问题五:iOS Safari 上自动播放被拦
症状:页面加载完了,但 SWF 不自动开始,要用户手动点一下画布才开始。
原因:iOS Safari 的自动播放策略——WebAssembly / Canvas 内容也受这个限制。Chrome 桌面和 Android Chrome 一般只拦音频,但 iOS 更严格。
解法:设计里假设「用户首次交互前不会自动播放」。加一个明显的「点击开始」按钮,用户点一下之后再 player.play() 或 player.load()。