fetch call behaves differently in a content script and in the background.
Extension.js does not change any of these rules. It does set headers on its own dev server, and it does patch the CSP during development. Both are covered below.
Where the request runs decides the rules
A content script shares the page’s origin for network purposes. Host permissions do not lift CORS there. This is the single most common surprise.
Move the request to the background
The reliable pattern is to ask the background to fetch, then send the result back:src/content/scripts.js
src/background.js
Declare the host permission
The background is exempt from CORS only for origins that the manifest names:manifest.json
<all_urls> works, and store reviewers ask about it.
Preflight requests still happen
A host permission removes the origin check on the response. It does not remove the preflight. A request that carries a custom header, or that uses a method beyondGET, HEAD, or POST, still sends an OPTIONS request first. The server must answer it. When you control the server, allow the method and the headers that you send. When you do not, keep the request simple.
The Extension.js dev server
Duringextension dev, Extension.js runs a local server that serves the reload client and the hot updates. Content scripts on any page dial it, which is itself a cross-origin request, so the server answers with:
Host header. This is a development server on your own machine. It never runs in a packaged extension, and nothing that it serves reaches production.
The dev-only CSP patch
Manifest v3 leavesconnect-src unrestricted unless you set it. A project that declares no content_security_policy therefore needs no patch, and the dev manifest carries the ordinary policy:
dist/chromium/manifest.json
connect-src of your own and the dev build appends the local origins to it, so your pages can still reach the socket:
dist/chromium/manifest.json
extension build. Your own connect-src survives, so a tight policy that forgets your API origin fails in production and passes in development. Read Manifest refusals when a CSP error survives into production.
Binding to another host
Bind the server somewhere else when you develop inside a container:0.0.0.0, so Extension.js resolves a connectable address for the client. It falls back to 127.0.0.1. Override that when the browser lives on another machine:
Remote scripts and stylesheets
Extension CSP forbids loading a script from a remote origin into an extension page. That is not CORS, and no header fixes it. Bundle the code instead. Extension.js reports this case during the build:Symptoms and fixes
Best practices
- Put every third-party request in the background, even the ones that work today.
- Name each origin in
host_permissionsrather than reaching for<all_urls>. - Test against a production build before you ship. Development is more permissive on purpose.
Next steps
- Learn how to pass data between contexts in Messaging.
- Review permissions and host permissions.
- Observe traffic with Intercept network requests.

