# How to Resolve CORS Extension Issues When Requests Originate from a Chrome Extension

> Resolve CORS extension issues for Chrome extensions. Learn why the Python requests library bypasses CORS and explore native messaging or server-side proxies for your extension.

- Repository: [Python Software Foundation/requests](https://github.com/psf/requests)
- Tags: how-to-guide
- Published: 2026-02-19

---

**The Python `requests` library is immune to CORS restrictions because it executes in the Python runtime outside the browser, while Chrome extensions must use native messaging or server-side proxies to bypass browser-enforced CORS policies.**

When building a Chrome extension that needs to fetch data from external APIs, developers often encounter Cross-Origin Resource Sharing (CORS) errors that block JavaScript `fetch` calls. This article explains why the `psf/requests` library never faces these restrictions and provides concrete solutions to resolve CORS extension issues in Chrome extensions.

## Why CORS Errors Occur in Chrome Extensions

CORS is a **browser security mechanism** that restricts JavaScript-initiated HTTP requests to different origins. When your Chrome extension runs `fetch()` or `XMLHttpRequest` from a content script or background service worker, the browser enforces CORS policies by checking for `Access-Control-Allow-Origin` headers in the response.

If the server does not include appropriate CORS headers, Chrome blocks the response, resulting in the infamous CORS error. This restriction exists solely within the browser's JavaScript engine and does not affect HTTP clients running in other environments.

## How the Python Requests Library Bypasses CORS

The `requests` library operates entirely within the Python runtime, completely outside the browser's security sandbox. According to the `psf/requests` source code, the library's architecture has no concept of CORS because it uses raw socket connections through `urllib3` rather than browser APIs.

### The Session Architecture

In [`src/requests/sessions.py`](https://github.com/psf/requests/blob/main/src/requests/sessions.py), the `Session` class manages connection pooling, cookie persistence, and request preparation. When you call `requests.get()`, the library creates a temporary `Session` instance that delegates to `requests.api.request()` in [`src/requests/api.py`](https://github.com/psf/requests/blob/main/src/requests/api.py). This flow executes pure Python HTTP logic without any browser intermediary.

### The HTTP Adapter Layer

The `HTTPAdapter` class in [`src/requests/adapters.py`](https://github.com/psf/requests/blob/main/src/requests/adapters.py) translates `PreparedRequest` objects into actual HTTP traffic using `urllib3` connections. This adapter handles redirects, proxy configuration, and SSL verification in [`src/requests/models.py`](https://github.com/psf/requests/blob/main/src/requests/models.py) and [`src/requests/exceptions.py`](https://github.com/psf/requests/blob/main/src/requests/exceptions.py), but it never inspects or enforces CORS headers because that concept does not exist in the Python HTTP stack.

## Solutions to Resolve CORS Extension Issues

Since the `requests` library avoids CORS by running outside the browser, you can resolve Chrome extension CORS issues by moving the HTTP logic out of the browser's JavaScript environment.

### Native Messaging with Python Requests

The most robust solution uses Chrome's native messaging API to delegate HTTP requests to a Python host application that uses `requests`.

First, configure your [`manifest.json`](https://github.com/psf/requests/blob/main/manifest.json) to declare the native messaging permission:

```json
{
  "name": "my-extension",
  "manifest_version": 3,
  "permissions": ["nativeMessaging"],
  "host_permissions": ["<all_urls>"]
}

```

Then implement the native host in Python using `requests` to perform the actual HTTP calls:

```python
#!/usr/bin/env python3

# native_host.py

import sys
import json
import struct
import requests

def read_message():
    """Read a message from Chrome extension via stdin."""
    raw_length = sys.stdin.buffer.read(4)
    if len(raw_length) == 0:
        sys.exit(0)
    length = struct.unpack('@I', raw_length)[0]
    message = sys.stdin.buffer.read(length).decode('utf-8')
    return json.loads(message)

def send_message(message):
    """Send a message back to Chrome extension via stdout."""
    encoded = json.dumps(message).encode('utf-8')
    length = struct.pack('@I', len(encoded))
    sys.stdout.buffer.write(length)
    sys.stdout.buffer.write(encoded)
    sys.stdout.buffer.flush()

def main():
    while True:
        try:
            msg = read_message()
            url = msg.get('url')
            method = msg.get('method', 'GET')
            
            # Use requests library - no CORS restrictions here

            if method == 'GET':
                resp = requests.get(url, timeout=10)
            else:
                resp = requests.post(url, json=msg.get('data'), timeout=10)
            
            send_message({
                'status': resp.status_code,
                'headers': dict(resp.headers),
                'body': resp.text
            })
        except Exception as e:
            send_message({'error': str(e)})

if __name__ == '__main__':
    main()

```

This approach completely bypasses browser CORS because the HTTP request originates from the Python process, not the browser's JavaScript engine.

### Server-Side Proxy Approach

If native messaging is too complex, deploy a lightweight server-side proxy using Flask or FastAPI that uses `requests` to fetch data and returns it to your extension:

```python
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)

@app.route('/proxy')
def proxy():
    target_url = request.args.get('url')
    # requests library handles the HTTP call without CORS concerns

    resp = requests.get(target_url, timeout=10)
    return jsonify({
        'status': resp.status_code,
        'content': resp.text
    })

if __name__ == '__main__':
    app.run(port=5000)

```

Your Chrome extension then calls `http://localhost:5000/proxy?url=https://api.example.com/data`, avoiding direct cross-origin requests to the target API.

### Configuring Host Permissions

For APIs that support CORS, ensure your [`manifest.json`](https://github.com/psf/requests/blob/main/manifest.json) includes the target host in `host_permissions` (Manifest V3) or `permissions` (Manifest V2):

```json
{
  "host_permissions": [
    "https://api.example.com/*"
  ]
}

```

However, this only works if the server sends appropriate `Access-Control-Allow-Origin` headers. The `requests` library does not require these headers because it operates outside the browser security model.

## Summary

- **CORS is browser-specific**: The security policy only applies to JavaScript running in web browsers, not to Python HTTP clients.
- **`requests` bypasses CORS by design**: The library in `psf/requests` uses `urllib3` through `HTTPAdapter` in [`src/requests/adapters.py`](https://github.com/psf/requests/blob/main/src/requests/adapters.py) to make raw HTTP connections without browser intervention.
- **Native messaging solves extension CORS**: Delegate HTTP calls to a Python native host using `requests` to completely avoid browser CORS restrictions.
- **Server proxies are an alternative**: A backend service using `requests` can fetch data and serve it to your extension without CORS errors.

## Frequently Asked Questions

### Why doesn't the Python requests library have CORS errors?

The `requests` library executes in the Python runtime environment, not inside a browser. CORS is a security policy enforced by browsers on JavaScript `fetch` and `XMLHttpRequest` calls. Since `requests` in [`src/requests/sessions.py`](https://github.com/psf/requests/blob/main/src/requests/sessions.py) uses `urllib3` to open direct TCP connections through `HTTPAdapter` in [`src/requests/adapters.py`](https://github.com/psf/requests/blob/main/src/requests/adapters.py), it never encounters browser CORS checks.

### Can I use Python requests directly in a Chrome extension?

No, you cannot import or execute Python code directly within a Chrome extension. Chrome extensions run JavaScript in the browser's V8 engine. However, you can use **native messaging** to communicate between your extension and a separate Python process running on the user's machine. This Python process can then use `requests` to perform HTTP calls without CORS restrictions.

### What is the best way to handle CORS in a Chrome extension when the API doesn't support it?

The most reliable method is to use a **native messaging host** written in Python that utilizes the `requests` library. This moves the HTTP request outside the browser entirely, bypassing CORS enforcement. Alternatively, implement a **server-side proxy** that your extension calls; the proxy uses `requests` to fetch the target data and returns it to the extension. Both approaches avoid browser CORS because the actual HTTP call happens in a Python environment.

### How do I configure Chrome extension permissions to avoid CORS errors?

Add the target domain to `host_permissions` in your [`manifest.json`](https://github.com/psf/requests/blob/main/manifest.json) (Manifest V3) or `permissions` (Manifest V2). However, this only works if the remote server sends the appropriate `Access-Control-Allow-Origin` headers. If the server does not support CORS, permissions alone cannot bypass the restriction—you must use a native host or proxy solution with `requests` instead.