No, it is possible with extensions.
Extensions can inject headers, such as Access-Control-Allow-Origin: *, to unblock cross-origin requests. In the Manifest V3 context, however, that might require patching window.fetch and window.XMLHttpRequest.
For example,
// content.js
window.fetch = async (...args) => {
const request = args[0] instanceof Request ? args[0].url : args[0]
const config = args[1] || {}
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage({ action: "proxyFetch", request, config }, response => {
if (response.error) {
const err = new Error(response.error.message)
err.name = response.error.name
err.stack = response.error.stack
if (response.error.cause) err.cause = response.error.cause
reject(err)
} else {
const base64Data = response.dataUrl.split(",")[1]
const bytes = Uint8Array.from(atob(base64Data), c => c.charCodeAt(0))
const contentType = response.headers["content-type"] || "application/octet-stream"
const blob = new Blob([bytes], { type: contentType })
const status = response.status
const statusText = response.statusText
const headers = new Headers(response.headers)
const body = status === 204 || status === 205 || status === 304 ? null : blob
resolve(new Response(body, { status, statusText, headers }))
}
})
})
}
// Background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "proxyFetch") {
fetch(message.request, message.config)
.then(async res => {
const headers = Object.fromEntries(res.headers.entries())
const blob = await res.blob()
const reader = new FileReader()
reader.onloadend = () =>
sendResponse({ status: res.status, statusText: res.statusText, headers, dataUrl: reader.result })
reader.readAsDataURL(blob)
})
.catch(err => {
const { name, message, code, stack } = err
sendResponse({ error: { name, message, code, stack } })
})
// Keeps the message channel open for the async fetch
return true
}
})
This is a cors bypass but part of this demo is that it's full Firefox including TLS support. Using this still means intercepting all requests in an inspectable medium and does defeat part of the point
This isn't enough for every website to load normally.