Skip to content

fix(tauri): return 404 for missing subresource assets instead of SPA index.html fallback - #15939

Open
gronke wants to merge 4 commits into
tauri-apps:devfrom
gronke:fix/asset-protocol-404-missing-subresources
Open

fix(tauri): return 404 for missing subresource assets instead of SPA index.html fallback#15939
gronke wants to merge 4 commits into
tauri-apps:devfrom
gronke:fix/asset-protocol-404-missing-subresources

Conversation

@gronke

@gronke gronke commented Aug 29, 2026

Copy link
Copy Markdown
Member

A request for a path missing from the bundled assets returned 200 text/html (the SPA index.html fallback), so a failed ES module import surfaced only as 'text/html' is not a valid JavaScript MIME type, naming neither the URL nor the cause.

  • AppManager::get_asset skips the whole fallback chain when the requested path has a static subresource extension (conservative allowlist in tauri_utils::mime_type::has_subresource_extension; dotted SPA routes like /product/v1.2 keep the fallback).
  • The tauri:// protocol answers 404 text/plain naming the path; other errors keep the 500 path. on_web_resource_request can still override.
  • The CLI's built-in dev server mirrors the same rule and 404 body.
  • The SPA fallback now logs a warning when it serves index.html for a missing path.
  • Fixes a latent double-boxing in get_asset's error path that made the boxed error non-downcastable to tauri::Error.

Header-based detection (Sec-Fetch-Dest) was deliberately not used; it is unreliable on WebKitGTK custom schemes. First unit tests for the get_asset chain (map-backed Assets impl) and the dev-server resolution helper.

Closes #15938

gronke added 2 commits August 28, 2026 17:59
…index.html fallback

A request for a missing path with a static subresource extension
(.js, .css, images, fonts, ...) previously resolved to index.html
with 200 text/html, breaking ES module imports with an opaque MIME
error that names neither the URL nor the cause. Applies to the
tauri:// protocol and the CLI's built-in dev server; the SPA fallback
for extensionless paths now logs a warning.
@gronke
gronke requested a review from a team as a code owner August 29, 2026 09:09
@gronke gronke added scope: webdriver tauri-driver tool and other items related to WebDriver type: developer experience labels Aug 29, 2026
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Package Changes Through 8cef582

There are 14 changes which include tauri with minor, tauri-cli with minor, @tauri-apps/cli with minor, tauri-build with minor, tauri-runtime with minor, tauri-runtime-wry with minor, tauri-utils with minor, tauri-bundler with minor, tauri-macos-sign with minor, @tauri-apps/api with minor, tauri-codegen with minor, tauri-macros with minor, tauri-plugin with minor, tauri-driver with minor

Planned Package Versions

The following package releases are the planned based on the context of changes in this pull request.

package current next
@tauri-apps/api 2.11.1 2.12.0
tauri-utils 2.9.3 2.10.0
tauri-macos-sign 2.3.4 2.4.0
tauri-bundler 2.9.4 2.10.0
tauri-runtime 2.11.3 2.12.0
tauri-runtime-wry 2.11.4 2.12.0
tauri-codegen 2.6.3 2.7.0
tauri-macros 2.6.3 2.7.0
tauri-plugin 2.6.3 2.7.0
tauri-build 2.6.3 2.7.0
tauri 2.11.5 2.12.0
@tauri-apps/cli 2.11.4 2.12.0
tauri-cli 2.11.4 2.12.0
tauri-driver 2.0.6 2.1.0

Add another change file through the GitHub UI by following this link.


Read about change files or the docs at github.com/jbolda/covector

…_test_module

Canonicalize the test dist dir like start() does; the scope check broke
on the macOS /var symlink and Windows short names. Move the test module
to the end of the file.
@gronke
gronke force-pushed the fix/asset-protocol-404-missing-subresources branch from 3438f2b to 4009af1 Compare August 29, 2026 17:34
@Legend-Master Legend-Master removed the scope: webdriver tauri-driver tool and other items related to WebDriver label Aug 31, 2026
Comment thread crates/tauri/src/manager/mod.rs Outdated
// a missing subresource (script, style, image, ...) must never resolve to
// an HTML document; skipping the fallback chain lets the protocol handler
// answer 404 instead of `index.html` with a misleading mime type
let use_fallbacks = !tauri_utils::mime_type::has_subresource_extension(&path);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not the most convinced with this, the whole point of this fallback is to always allow the client to react to the URL. Unless we can be sure that this is not from a navigation request, we should not do this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and you are right that the extension heuristic could not answer it; a navigation to /reports/2024.pdf would have 404'd.

Reworked to decide from the request destination rather than the URL.
Measured what actually reaches the tauri:// handler on WebKitGTK 2.52.3: no Sec-Fetch-Dest for custom protocols, but Accept is text/html,application/xhtml+xml,… for navigations (including /reports/2024.pdf) and */* or image/… for scripts, images and fetch.

The rule is now: Sec-Fetch-Dest when the webview sends it (document/iframe/frame → navigation), otherwise Accept containing text/html, and if neither header is present the request counts as a navigation so the fallback is preserved. Navigations therefore always reach the frontend router, and only a request that does not present itself as a document load can 404.

This also removes the extension allowlist entirely, and AssetResolver keeps the old behaviour since it has no request to classify.

Only navigations resolve to the SPA index.html fallback, decided by
Sec-Fetch-Dest where the webview sends it and by the Accept header
otherwise, with no header evidence meaning navigation. Replaces the
subresource extension allowlist, so a navigation to a document-looking
path such as /reports/2024.pdf still reaches the frontend router while
a missing script, style or image gets a 404. AssetResolver has no
request to classify and keeps the historical fallback.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Asset protocol: a missing file returns 200 text/html, so a failed module import names neither the URL nor the cause

2 participants