You can't read the redirect location directly. As a workaround, you can navigate the browser to the opaqueredirect
response, if you store it in a cache and use a service worker to replay the response. See an example below with one same-origin redirect followed by two cross-origin redirects.
index.html
:
<!DOCTYPE html>
<html lang="en">
<head><title>Navigate to opaqueredirect</title></head>
<body>
<button id=button disabled>Navigate to opaqueredirect</button>
</body>
<script>
navigator.serviceWorker.register('sw.js');
navigator.serviceWorker.ready.then(() => {
button.disabled = false;
button.onclick = () => {
fetch('/redirect1', { redirect: 'manual' }).then(
(response) => {
if (response.type === 'opaqueredirect') {
const urlToCache = new URL(response.url);
const uuid = crypto.randomUUID();
urlToCache.searchParams.set('cacheId', uuid);
const requestToCache = new Request(urlToCache);
caches.open('cacheId').then((cache) => {
cache.put(requestToCache, response).then(() => {
window.location.assign(urlToCache);
}).catch(console.error);
}).catch(console.error);
}
},
).catch(console.error);
};
});
</script>
</html>
serve.json
:
{
"redirects": [
{
"source": "/redirect1",
"destination": "/redirect2",
"type": 302
},
{
"source": "/redirect2",
"destination": "https://mock.httpstatus.io/307",
"type": 302
}
]
}
sw.js
:
self.addEventListener('fetch', (event) => {
if (new URL(event.request.url).searchParams.get('cacheId')) {
event.respondWith(caches.open('cacheId').then(async (cache) => {
const cacheMatch = await cache.match(event.request);
if (cacheMatch) {
cache.delete(event.request).then(() => { }).catch(console.error);
return cacheMatch;
}
return fetch(event.request);
}));
}
});
Run npx -y serve
in a directory containing the above files, then open localhost:3000
in your browser. Then click the button and the redirect will be followed to https://mock.httpstatus.io/200
.