`fetch()` fails or returns an opaque response for a redirected cross-origin URL
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 7.7k
- Forks
- 879
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 68
Description
Bug Description
fetch() fails or returns an opaque response when it takes a redirected cross-origin URL, while it always returns a basic (transparent) response when the cross-origin URL is not redirected.
Reproducible By
Test results 1-3 are expected. Test results 4-9 are not?
import http from 'http'
import { fetch, setGlobalOrigin } from 'undici'
function createServer(same, cross) {
http.createServer((req, res) => {
if (req.url === '/redirected') {
return res.writeHead(301, { Location: `http://localhost:${cross}` }).end()
}
return res.writeHead(200).end()
})
.listen(same)
}
createServer(8080, 8081)
createServer(8081, 8080)
const origin = 'http://localhost:8080'
const crossOrigin = 'http://localhost:8081'
setGlobalOrigin(origin)
const tests = [
// Not redirected
[`${crossOrigin}/foo`], // 1. basic
[`${crossOrigin}/foo`, { mode: 'same-origin' }], // 2. basic
[`${crossOrigin}/foo`, { mode: 'no-cors' }], // 3. basic
// Redirected from cross-origin to same-origin
[`${crossOrigin}/redirected`], // 4. cors
[`${crossOrigin}/redirected`, { mode: 'same-origin' }], // 5. fetch failed
[`${crossOrigin}/redirected`, { mode: 'no-cors' }], // 6. opaque
// Redirected from same-origin to cross-origin
[`${origin}/redirected`], // 7. cors
[`${origin}/redirected`, { mode: 'same-origin' }], // 8. fetch failed
[`${origin}/redirected`, { mode: 'no-cors' }], // 9. opaque
]
.map(async ([url, init]) => {
try {
const response = await fetch(url, init)
return response.type
} catch (e) {
return e.message
}
})
const results = await Promise.all(tests)
console.log(results)
Note that for browsers, returning an opaque response to a request of a cross-origin URL redirected to a same-origin URL is expected (https://github.com/whatwg/fetch/issues/737).
Expected Behavior
I would expect fetch() to allow requests to any origin regardless of any redirection:
Unlike browsers, Undici does not implement CORS (Cross-Origin Resource Sharing) checks by default. This means:
- [...]
- Requests to any origin are allowed regardless of the source
https://undici.nodejs.org/#/?id=specification-compliance
Logs & Screenshots
Environment
Node v24.12.0, Undici v7.18.2
Additional context
While debugging undici.fetch() taking a redirected URL, this line in mainFetch() intrigued me, because it does not match the origin of the current URL with request.origin, as required in the spec, but against the origin of request.url:
request’s current URL’s origin is same origin withrequest’s origin, andrequest’s response tainting is"basic"
I am not very familiar with the Fetch specification but I guess this difference is intentional.
Now assuming consistency between non-redirected and redirected cross-origin request is a valid expectation, I do not know how much undici could be allowed to deviate from the specification.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at lib/web/fetch/index.js, particularly mainFetch() around the linked origin check, and run the provided HTTP-server reproduction with Undici's fetch(). Compare redirected and non-redirected behavior with the Fetch specification and determine the intended handling; done means the behavior is resolved consistently and covered by regression tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100