SvelteKit重新部署后访问报Unexpected token '<'错误如何解决
根因说明
Unexpected token '<'是SvelteKit搭配static adapter部署后非常典型的缓存与资源版本不匹配问题,触发逻辑非常明确:
- SvelteKit每次构建会为带内容哈希的JS、CSS等静态资源生成全新文件名,同时更新入口HTML文件内的资源引用路径
- 新版本部署后,如果用户本地缓存了旧版的入口HTML,而旧版HTML中引用的带哈希JS资源已经被新部署覆盖删除,浏览器发起旧JS资源请求时,静态托管服务会因为找不到对应文件,默认fallback返回入口HTML的内容(以
<!DOCTYPE html>开头,首个有效字符为<) - 浏览器预期返回JS脚本,实际拿到HTML内容,解析时碰到首个字符
<就会抛出该错误。手动刷新时浏览器会重新拉取最新版入口HTML,拿到正确的新资源引用路径后问题就会消失。
可落地的修复方案
按优先级落地即可彻底解决问题:
- 配置分层缓存规则
对所有文件名带构建哈希的静态资源(JS、CSS、字体、图片等),设置响应头
Cache-Control: public, max-age=31536000, immutable,这类资源文件名和内容强绑定,内容变更时文件名一定会同步变化,可以放心长期缓存
对入口index.html以及所有路由对应的无哈希HTML文件,设置响应头Cache-Control: no-cache,要求浏览器每次访问都校验文件版本,禁止长期缓存入口文件 - 修正静态服务的fallback逻辑
不要对所有不存在的请求统一返回200状态的入口HTML,针对JS、CSS、图片等静态资源类型的请求,找不到对应文件时直接返回404状态码即可,避免把HTML内容当成脚本返回。以Nginx为例的配置参考:location / { try_files $uri $uri/ /index.html; } location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { try_files $uri =404; expires 1y; add_header Cache-Control "public, immutable"; } - 调整CI部署的资源清理逻辑
不要在部署时全量清空静态资源目录后再上传新构建产物,保留历史构建生成的带哈希静态资源即可——这类资源文件名唯一不会冲突,即使部分用户还持有旧版入口HTML,也能正常加载到对应旧版脚本,不会触发资源不存在的fallback - 可选:增加前端自动兜底逻辑
在SvelteKit的app.html中加入全局错误监听,捕获到脚本加载解析错误时自动触发硬刷新,不需要用户手动操作:<script> window.addEventListener('error', (e) => { const isScriptLoadFail = e.target.tagName === 'SCRIPT'; const isParseError = e.message?.includes('Unexpected token \'<\''); if (isScriptLoadFail && isParseError) { location.reload(true); } }, true); </script>
内容的提问来源于stack exchange,提问作者David Dal Busco
相关产品推荐
相关产品推荐

