[bug] request got a 403 response using tauri_plugin_http and unsafe_headers feature
Describe the bug
I'm using tauriv2 and tauri_plugin_http in a tauri + vue + rust project.
The url example: https://api.bilibili.com/x/web-interface/nav
when I use curl, it works fine:
$curl https://api.bilibili.com/x/web-interface/nav
{"code":-101,"message":"账号未登录","ttl":1,"data":{"isLogin":false,"wbi_img":{"img_url":"https://i0.hdslb.com/bfs/wbi/7cd084941338484aae1ad9425b84077c.png","sub_url":"https://i0.hdslb.com/bfs/wbi/4932caff0ff746eab6f01bf08b70ac45.png"}}}
reqwest also works
use reqwest::{get, Response};
#[tokio::main]
async fn main() {
let res: Response = get("https://api.bilibili.com/x/web-interface/nav").await.unwrap();
println!("{:?}", res.text().await);
}
C:/Users/Admin/.cargo/bin/cargo.exe run --color=always --profile dev --package test_ --bin test_
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
Running `target\debug\test_.exe`
Ok("{\"code\":-101,\"message\":\"账号未登录\",\"ttl\":1,\"data\":{\"isLogin\":false,\"wbi_img\":{\"img_url\":\"https://i0.hdslb.com/bfs/wbi/7cd084941338484aae1ad9425b84077c.png\",\"sub_url\":\"https://slb.com/bfs/wbi/4932caff0ff746eab6f01bf08b70ac45.png\"}}}")
Process finished with exit code 0
but I always got a 403 response when using tauri_plugin_http
tauri-plugin-http = { version = "2.0.0-rc", features = [
"unsafe-headers",
] }
const response = await fetch(
'https://api.bilibili.com/x/web-interface/nav',
{
method: 'GET',
}
)
console.log(response.status) // e.g. 200
console.log(response.statusText) // e.g. "O
console.log(response.text()) // e.g. { "key": "value" }
I can see the request and response through ipc in the Console, if no features, it works well.
http://ipc.localhost/plugin%3Ahttp%7Cfetch
{"clientConfig":{"method":"GET","url":"https://api.bilibili.com/x/web-interface/nav","headers":[],"data":null}}
http://ipc.localhost/plugin%3Ahttp%7Cfetch_send
{"status":200,"statusText":"OK","headers":[["date","Tue, 22 Oct 2024 19:52:41 GMT"],["content-type","application/json; charset=utf-8"],["content-length","242"],["connection","keep-alive"],["bili-status-code","-101"],["bili-trace-id","348876bfbf671802"],["cpu_usage","50"],["x-bili-trace-id","4869bda0d230ca31348876bfbf671802"],["x-ticket-status","1"],["access-control-expose-headers","X-Cache-Webcdn"],["expires","Tue, 22 Oct 2024 19:52:40 GMT"],["cache-control","no-cache"],["x-cache-webcdn","BYPASS from blzone03"]],"url":"https://api.bilibili.com/x/web-interface/nav","rid":1556556238}
If add features = [ "unsafe-headers" ], then get a wrong response.
http://ipc.localhost/plugin%3Ahttp%7Cfetch
{"clientConfig":{"method":"GET","url":"https://api.bilibili.com/x/web-interface/nav","headers":[],"data":null}}
http://ipc.localhost/plugin%3Ahttp%7Cfetch_send
{"status":403,"statusText":"Forbidden","headers":[["date","Tue, 22 Oct 2024 19:14:57 GMT"],["content-type","text/html"],["transfer-encoding","chunked"],["connection","keep-alive"],["server","openresty"],["etag","W/\"6708c0f4-dca\""]],"url":"https://api.bilibili.com/x/web-interface/nav","rid":2481797906}
Full tauri info output
[✔] Environment
- OS: Windows 10.0.26100 x86_64 (X64)
✔ WebView2: 130.0.2849.46
✔ MSVC: Visual Studio Community 2022
✔ rustc: 1.82.0 (f6e511eec 2024-10-15)
✔ cargo: 1.82.0 (8f40fc59f 2024-08-21)
✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
- node: 20.18.0
- pnpm: 9.12.1
- npm: 10.8.2
- bun: 1.1.31
[-] Packages
- tauri 🦀: 2.0.2
- tauri-build 🦀: 2.0.1
- wry 🦀: 0.44.1
- tao 🦀: 0.30.2
- @tauri-apps/api : 2.0.0-rc.6 (outdated, latest: 2.0.3)
- @tauri-apps/cli : 2.0.0-rc.17 (outdated, latest: 2.0.4)
[-] Plugins
- tauri-plugin-dialog 🦀: 2.0.1
- @tauri-apps/plugin-dialog : 2.0.0 (outdated, latest: 2.0.1)
- tauri-plugin-store 🦀: 2.0.0-rc.3
- @tauri-apps/plugin-store : 2.0.0-rc.1 (outdated, latest: 2.1.0)
- tauri-plugin-fs 🦀: 2.0.1
- @tauri-apps/plugin-fs : not installed!
- tauri-plugin-http 🦀: 2.0.1
- @tauri-apps/plugin-http : 2.0.1
- tauri-plugin-log 🦀: 2.0.1
- @tauri-apps/plugin-log : 2.0.0
- tauri-plugin-shell 🦀: 2.0.0-rc.3
- @tauri-apps/plugin-shell : 2.0.0-rc.1 (outdated, latest: 2.0.1)
[-] App
- build-type: bundle
- CSP: unset
- frontendDist: ../dist
- devUrl: http://localhost:1420/
- framework: Vue.js
- bundler: Rollup
Same Problems with me.
@imxuedi Can you also share the output of the tauri info command please? And maybe the fetch() call as well
The output of tauri info
[✔] Environment
- OS: Windows 10.0.26100 x86_64 (X64)
✔ WebView2: 131.0.2903.112
✔ MSVC: Visual Studio Community 2022
✔ rustc: 1.81.0 (eeb90cda1 2024-09-04)
✔ cargo: 1.81.0 (2dbb1af80 2024-08-20)
✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
- node: 20.15.0
- yarn: 1.22.22
- npm: 10.7.0
[-] Packages
- tauri 🦀: 2.1.1
- tauri-build 🦀: 2.0.3
- wry 🦀: 0.47.2
- tao 🦀: 0.30.8
- tauri-cli 🦀: 2.1.0
- @tauri-apps/api : 2.1.1
- @tauri-apps/cli : 2.1.0
[-] Plugins
- tauri-plugin-single-instance 🦀: 2.2.0
- @tauri-apps/plugin-single-instance : not installed!
- tauri-plugin-global-shortcut 🦀: 2.2.0
- @tauri-apps/plugin-global-shortcut : 2.2.0
I export fetch func to window with alias mfetch, as you can see:
import { fetch } from "@tauri-apps/plugin-http";
window.mfetch = fetch;
I also add the following permissions in capabilities/default.json
"http:allow-fetch",
{
"identifier": "http:default",
"allow": [
{
"url": "http://10.8.51.249:11435/*"
}
]
},
The same request by curl in powershell
Only happened in release mode, fetch works well in debug mode.
Adding features= ["unsafe-headers"] also doesn't help.
Adding features= ["unsafe-headers"] also doesn't help.
then your problem is unrelated to this issue here btw.
If this only happens in release mode, could the server deny requests from the tauri://localhost and/or http://tauri.localhost origins (depending on the os)? If this is the issue, and you can't change the server, you could also try to remove the origin header from the request by adding the unsafe-headers flag and then setting the Origin header in the request to an empty string (the plugin has special handling for an empty string and will completely remove the header then)
Same Problems with me.
Bit late to the game but I have experienced the same. From your url I am assuming you are talking to Ollama? If this is so you can set the OLLAMA_ORIGINS environment variable to allow different origin like this:-
OLLAMA_ORIGINS=http://tauri.localhost
Ollama will, by default, only allow a small set of origins.
The version I am using allows these:-
OLLAMA_ORIGINS:[http://localhost https://localhost http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1 http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0 http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://* vscode-webview://*]
Greetings, fellow developers!
After reading through this thread, I managed to resolve the issue, and I wanted to share my experience to help others who might encounter a similar problem. My scenario is almost identical to what @Z-Only described.
In my case, the exact same request worked perfectly with cURL but failed when using fetch. Inspired by @FabianLars' suggestion that the headers.origin might be the culprit, I tested adding --header 'origin: https://www.google.com' to my cURL command. True enough it also resulted in the same 403 Forbidden error with the message "Invalid CORS request.", this indicates that the issue is not limited to just the fetch API.
Interestingly, the backend API responded with access-control-allow-origin: *, so at first glance, it seemed CORS was set up correctly. However, for some reason, the fetch request still failed with a 403. This might indicate an issue with the backend API, but that's beyond my control.
What I could do on the client side, though, was omit the headers.origin, and that turned out to be the solution. Here's how I resolved the issue:
-
Updated
@tauri-apps/plugin-httpto2.2.0 -
Added the following dependency in
Cargo.toml:tauri-plugin-http = { version = "2.2.0", features = ["unsafe-headers"] } -
Initialize
headers.Originto an empty string prior tofetchcall:async function request(token: string, headers?: Record<string, unknown>): Promise<any> { // Setting the 'headers.Origin' property to an empty string to handle CORS-related // issues. The '@tauri-apps/plugin-http' library removes the 'Origin' header when // its value is set to an empty string. This is crucial because our API responds // with a "403 Forbidden" error (and a response body stating "Invalid CORS Request") // if the 'Origin' header contains values like "http://localhost:1234". Removing the // 'Origin' header in the request helps ensure the API functions as expected without // triggering CORS validation errors. // headers = headers ?? {}; headers.Origin = ""; // Added this line to initialize `Origin` to empty string const response = await fetch(this._url, { method: this._method, headers: { Authorization: `Bearer ${token}`, ...headers, }, }); // ... } -
And viola! That call became successful!
Here's my Tauri info just in case it matters:
[✔] Environment
- OS: Windows 10.0.19045 x86_64 (X64)
✔ WebView2: 131.0.2903.112
✔ MSVC:
- Visual Studio Build Tools 2022
- Visual Studio Professional 2019
- Visual Studio Professional 2022
✔ rustc: 1.82.0 (f6e511eec 2024-10-15)
✔ cargo: 1.82.0 (8f40fc59f 2024-08-21)
✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
- node: 22.12.0
- yarn: 1.22.19
- npm: 11.0.0
[-] Packages
- tauri 🦀: 2.0.6
- tauri-build 🦀: 2.0.2
- wry 🦀: 0.46.3
- tao 🦀: 0.30.3
- @tauri-apps/api : 2.0.3
- @tauri-apps/cli : 2.0.5
[-] Plugins
- tauri-plugin-window-state 🦀: 2.0.1
- @tauri-apps/plugin-window-state : 2.0.0
- tauri-plugin-positioner 🦀: 2.0.2
- @tauri-apps/plugin-positioner : 2.0.1
- tauri-plugin-http 🦀: 2.2.0
- @tauri-apps/plugin-http : 2.2.0
- tauri-plugin-store 🦀: 2.1.0
- @tauri-apps/plugin-store : 2.1.0
- tauri-plugin-fs 🦀: 2.2.0
- @tauri-apps/plugin-fs : not installed!
- tauri-plugin-shell 🦀: 2.0.2
- @tauri-apps/plugin-shell : 2.0.1
[-] App
- build-type: bundle
- CSP: unset
- frontendDist: ../dist
- devUrl: http://localhost:1420/
- framework: React
- bundler: Vite
Why does the fetch in tauri-plugin-http still have an Origin header?
because the plugin by default tries to follow the native fetch as behavior as best as it can 🤷 (or something like that idk, wasn't really taking part in the decision making there)