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-is the CSP token that lets the WebView render the URL.src . . . asset: http: / / asset. localhost asset:covers macOS/Linux;http:is the Windows form of the same protocol. Omit either and the image is blocked by CSP even when the scope allows the file./ / asset. localhost
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:in/ / asset. localhost img-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:-- the cross-platform tokens that let the WebView render the URL./ / asset. localhost 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.