它是怎么工作的?
在 Tauri 应用中,前端页面运行在系统 WebView 里,而页面资源(HTML、JS、CSS 等)是由 Tauri 的内部协议提供的。当你在配置中定义了 HTTP 头后,Tauri 会在每次通过内部 get_response 函数发送响应时附带这些头。
不过有两个例外:IPC 消息 和错误响应不会包含这些自定义头。这是合理的设计------IPC 通信属于内部机制,不需要也不应该被前端的安全策略所干扰。
支持哪些头?
出于安全考虑,Tauri 并非允许设置任意 HTTP 头,而是限定了一个白名单。这些头可以大致分为三类。
CORS 相关头 :用于控制跨源资源共享行为,包括 Access-Control-Allow-Credentials、Access-Control-Allow-Headers、Access-Control-Allow-Methods、Access-Control-Expose-Headers 和 Access-Control-Max-Age。
跨源隔离策略头 :这是最常用的一组,包括 Cross-Origin-Embedder-Policy、Cross-Origin-Opener-Policy 和 Cross-Origin-Resource-Policy。它们的典型用途是启用需要跨源隔离的 Web API,比如 SharedArrayBuffer。
其他安全与功能头 :Permissions-Policy 用于控制浏览器特性的访问权限,Timing-Allow-Origin 控制哪些源可以访问资源时序数据,X-Content-Type-Options 防止 MIME 类型嗅探,Service-Worker-Allowed 控制 Service Worker 的作用范围。
此外还有一个 Tauri-Custom-Header,但它仅供测试使用,不建议在生产环境中出现。
有一点需要特别注意:CSP(Content-Security-Policy)不在这里配置 。CSP 有自己独立的 csp 配置字段,两者是完全分开的。
四种配置语法
HTTP Headers 在 tauri.conf.json 的 app.security.headers 字段下定义,支持四种值格式,每种格式对应不同的处理规则。
字符串:值原样输出。适合大多数只需要单个值的头。
json
"Cross-Origin-Opener-Policy": "same-origin"
生成结果:cross-origin-opener-policy: same-origin
数组:各项以逗号连接。适合需要列出多个值的场景。
json
"Timing-Allow-Origin": [
"https://developer.mozilla.org",
"https://example.com"
]
生成结果:timing-allow-origin: https://developer.mozilla.org, https://example.com
对象(键值对) :每个键值对以 key value 格式组合,各对之间用分号连接。适合结构化的头值。
css
"Tauri-Custom-Header": {
"key1": "'value1' 'value2'",
"key2": "'value3'"
}
生成结果:tauri-custom-header: key1 'value1' 'value2'; key2 'value3'
null:该头会被完全忽略,不出现在响应中。当你需要临时禁用某个头时非常方便。
json
"X-Content-Type-Options": null
完整配置示例
将所有格式组合在一起,一个典型的配置如下:
json
{
"app": {
"security": {
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
"Timing-Allow-Origin": [
"https://developer.mozilla.org",
"https://example.com"
],
"X-Content-Type-Options": null,
"Access-Control-Expose-Headers": "Tauri-Custom-Header",
"Tauri-Custom-Header": {
"key1": "'value1' 'value2'",
"key2": "'value3'"
}
},
"csp": "default-src 'self'; connect-src ipc: http://ipc.localhost"
}
}
}
这份配置最终生成的响应头完整呈现如下:
perl
access-control-allow-origin: http://tauri.localhost
access-control-expose-headers: Tauri-Custom-Header
content-security-policy: default-src 'self'; connect-src ipc: http://ipc.localhost; script-src 'self' 'sha256-...'
content-type: text/html
cross-origin-embedder-policy: require-corp
cross-origin-opener-policy: same-origin
tauri-custom-header: key1 'value1' 'value2'; key2 'value3'
timing-allow-origin: https://developer.mozilla.org, https://example.com
可以注意到几个细节:X-Content-Type-Options 因为设为 null 而没有出现;access-control-allow-origin 和 content-type 是 Tauri 自动添加的;content-security-policy 来自独立的 csp 字段,且 Tauri 自动附加了脚本的 SHA 哈希值。
最常见的用途:启用 SharedArrayBuffer
如果你的应用需要使用 SharedArrayBuffer(比如在 WebAssembly 多线程场景下),现代浏览器要求页面处于"跨源隔离"状态。实现方式就是同时设置以下两个头:
json
{
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
Cross-Origin-Opener-Policy: same-origin 确保页面只与同源页面共享浏览上下文组,而 Cross-Origin-Embedder-Policy: require-corp 要求所有嵌入的跨源资源必须显式声明允许被嵌入。两者结合后,浏览器认为页面已处于隔离环境,就会解锁 SharedArrayBuffer 等受限 API。
开发模式的坑:框架配置同步
这里有一个非常容易踩的坑。Tauri 的 headers 配置只在构建产物中生效------在开发模式下,前端资源由框架的开发服务器提供,Tauri 无法向其注入头信息。
这意味着如果你的应用依赖这些头才能正常运行(典型的就是 SharedArrayBuffer 场景),你需要在框架配置中也设置相同的头,确保开发和生产行为一致。
Vite 系项目(React、Vue、Svelte、Solid、Qwik)
arduino
// vite.config.ts
import { defineConfig } from 'vite';
export default defineConfig({
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
});
Nuxt
Nuxt 本身基于 Vite,但配置位置不同,需要在 nuxt.config.ts 中嵌套:
php
// nuxt.config.ts
export default defineNuxtConfig({
vite: {
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
},
});
Angular
Angular 不使用 Vite,需要在 angular.json 中配置:
json
{
"projects": {
"your-project": {
"architect": {
"serve": {
"options": {
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
}
}
}
}
}
Next.js
Next.js 也不使用 Vite,通过 next.config.js 的 headers() 函数配置:
javascript
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/*',
headers: [
{ key: 'Cross-Origin-Opener-Policy', value: 'same-origin' },
{ key: 'Cross-Origin-Embedder-Policy', value: 'require-corp' },
],
},
];
},
};
Rust 前端(Yew、Leptos)
使用 Trunk 作为构建工具的 Rust 前端框架,在 Trunk.toml 中配置:
ini
[serve]
headers = {
"Cross-Origin-Opener-Policy" = "same-origin",
"Cross-Origin-Embedder-Policy" = "require-corp",
}
小结
Tauri 2.1 引入的 HTTP Headers 配置功能,填补了此前 WebView 响应头不可控的空白。虽然可配置的头名称有限,但覆盖了跨源隔离、CORS 控制、权限策略等最核心的安全场景。
在使用这一功能时,记住三个要点:CSP 不在 headers 中配置,它有自己的独立字段;开发模式和构建模式需要分别在框架配置和 Tauri 配置中设置头信息;白名单之外的头名称不会生效,这是刻意为之的安全限制。
将 HTTP Headers 与 Capabilities、CSP 配合使用,可以从传输层、API 层、内容层三个维度构建起完整的安全防线------这正是 Tauri 纵深防御设计哲学的体现。