Asset Protocol and CSP Scope
The WebView config behind convertFileSrc -- the csp null shortcut, the assetProtocol scope allowlist, and why images go blank in a hardened build.
What this page covers
convertFileSrc turns a local file path into an asset:// URL the WebView can load directly (used for thumbnails, image previews, anything you don’t want to round-trip through IPC as base64).
Whether that URL actually loads is decided entirely by WebView config — specifically the Content Security Policy and the asset protocol scope in tauri.conf.json. This page is about that config layer. (The Rust-side convertFileSrc usage and the path/base64 decision live on a separate page.)
The csp: null shortcut
Many small/internal apps take the shortcut of disabling CSP entirely. With no CSP, asset:// URLs load with no allowlist — you don’t need an assetProtocol block at all:
{
"app": {
"security": {
"csp": null
}
}
}
That is the entire security block in a real image-viewer app: csp: null, no assetProtocol scope. convertFileSrc returns a URL and the WebView loads it from anywhere on disk.
⚠️ Warning
"csp": null disables Content Security Policy completely. The WebView can load scripts, styles, and files from any origin and any path on disk. Fine for a local dev tool you control; not acceptable for a hardened, distributable app.
The hardened build: scope + tightened CSP
For a distributable build you turn CSP back on. The moment you do, the asset protocol stops being implicitly open — you must enable it and declare a scope, and you must allowlist asset: in the CSP.
Enable the protocol and scope which paths it may serve:
{
"app": {
"security": {
"csp": "default-src 'self'; img-src 'self' asset: http://asset.localhost blob: data:",
"assetProtocol": {
"enable": true,
"scope": ["$APPDATA/images/**", "$HOME/Pictures/**"]
}
}
}
}
Two things are doing the work here:
assetProtocol.scopeis an array of glob patterns (it usesFsScope, the same path-glob mechanism as the fs plugin). Only files matching a pattern can be served. Tauri path variables like$APPDATA,$HOME,$RESOURCEare expanded.img-src ... asset: http://asset.localhostis the CSP token that lets the WebView render the URL.asset:covers macOS/Linux;http://asset.localhostis the Windows form of the same protocol. Omit either and the image is blocked by CSP even when the scope allows the file.
The trap: works in dev, blank in a locked-down build
This is the classic failure mode. During development you ran with csp: null, so convertFileSrc images loaded fine. You then ship a hardened config with a real CSP — and every asset image renders blank, with no error dialog and no thrown exception. The only signal is a CSP violation in the WebView devtools console.
The cause is one of two missing pieces:
- No
asset:/http://asset.localhostinimg-src— the CSP blocks the URL before the file is ever read. - The file isn’t inside
assetProtocol.scope— the protocol refuses to serve it.
📝 Note
The failure is silent because a blocked <img> does not throw; the element just shows nothing. If asset images “disappeared” the moment you tightened CSP, check the devtools console for a CSP img-src violation first, then verify the file path matches a scope glob.
Key takeaways
csp: nullopens the asset protocol with no allowlist — the real-world shortcut, fine only for local tools.- A real CSP requires
assetProtocol.enable: trueplus ascope— the protocol is off by default once CSP is active. img-srcmust listasset:andhttp://asset.localhost— the cross-platform tokens that let the WebView render the URL.- The dev-to-prod trap is silent — images that worked under
csp: nullgo blank under a strict CSP with no error; check the devtools console.
See also
The Image Viewer App recipe shows how convertFileSrc and the asset protocol are used end-to-end in a real app with HEIC decoding and a two-tier thumbnail cache.