你随手选了某个开箱即用的作品集模板,把代码一推就去刷推了。可你的访客呢?他们刚点开页面,浏览器就悄悄给谷歌的字体服务器打了个招呼,捎上了自己的IP地址和Referer信息。与此同时,页面里散落的内联脚本和分析工具,甚至可能还没等你弹出同意弹窗,就已经把Cookie塞进了用户磁盘。这样的站点,要给它配个靠谱的内容安全策略(CSP),骨子里就是松垮的,有也等于没有。
我就想让情况反过来:一个从第一行代码就开始保护隐私的站点,快、私密,并且在securityheaders.com上直接拿高分——不需要反复调参。我用的武器是Astro,下面就把这套“默认安全”的配方还原给你看,每一步都不需要黑客级操作。
Astro天生就帮我们卸掉了一大块包袱。它构建时把组件编译成静态HTML,默认发出去的页面里一行JavaScript都不带。只有当某个交互组件你真的需要客户端运行时,你才手动打开开关,选择加载对应的脚本。换句话说,攻击面天然就小——页面里可执行的代码越少,你的CSP就能收得越紧,还不容易误伤正常功能。
要拿下安全头部的最高分,最直接的杀手锏就是一条严格的CSP响应头。我的策略只用一句话概括:所有资源只信任自己的域名。最核心的一句是default-src 'self',这告诉浏览器,所有类型资源都默认从自身域名加载,其余指令再根据需要微调。比如脚本源同样设为仅自身域名,并且刻意不加入unsafe-inline。别小看这处省略,它直接堵死了整类内联脚本注入跨站攻击(XSS)的大门。就算攻击者成功在页面里插进了一对script标签,只要你的CSP里没写unsafe-inline,注入的代码根本跑不起来。交换条件只有一个:你自己也不能再写任何内联脚本和事件属性了。
Astro把这件麻烦事处理得很丝滑。你在.astro组件里写的script标签,并不是原样扔到浏览器,而是在构建阶段就被抽离成独立的外部文件,文件名还带了哈希值,最终全由你自己的域名提供。这些外链脚本天然就满足脚本同源的要求,你什么都不用调。
你需要留意的就是戒掉onclick这类属性,也不要在页面里直接写内联的script代码块——把所有逻辑放进单独的.js或.ts文件里,用addEventListener挂载。主题切换、视图过渡动画,同理,别在HTML里随手写个内联block,完整地交给一个外部脚本管理。
样式那头的策略,我稍微放松了点。因为Astro会给组件生成一些作用域样式,以内部style标签的形式存在,所以样式源指令里我保留了unsafe-inline。好在样式无法执行恶意代码,相比脚本,这个缺口带来的风险几乎可以忽略。如果你非要追求理论上的绝对严格,当然可以把样式也外部化或者用哈希值匹配,但安全收益的边际很小。你的“严格度预算”还是应该重点砸在脚本源上。
搞完CSP,该收拾字体请求了。多少网站一句@import就把https://fonts.googleapis.com挂在了head里,结果每一个访客打开页面,都得先跟谷歌的服务器握个手。这一趟来回,泄露了IP和Referer不说,还额外增加了一次DNS查询和TCP连接,卡在首屏渲染的关键路径上。想同时拿下隐私和速度,办法只有一个:自己托管字体。
整个过程比想象中简单。先把字体的woff2文件搞到手——直接从字体的GitHub仓库扒,或者用google-webfonts-helper这类辅助工具都行。接着把这些文件丢进项目的public/fonts/目录。然后写一段本地@font-face声明:
@font-face { font-family: 'Inter'; src: url('/fonts/inter-variable.woff2') format('woff2'); font-weight: 100 900; font-display: swap;}为了让首屏文字不出现空白闪烁,再把关键字体文件的preload链接补上,放在head中:
这样,字体完全由你自己的服务器提供,不再有任何第三方依赖,隐私和速度双双到手。
热门跟贴