Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
If a page opens normally but Python Selenium reports a CORS error, Selenium is usually not the cause. The page navigation succeeded; a JavaScript fetch() or XMLHttpRequest then tried to read a response from a different origin that the API did not authorize. Find that exact request in the browser console and Network panel, then fix the server policy or move the authorized API call to a suitable server-side design.
Why a working page can still produce a CORS error
Selenium WebDriver drives a real browser. Scripts running inside that browser remain subject to the same-origin policy and Cross-Origin Resource Sharing (CORS). WebDriver does not grant page JavaScript permission to read cross-origin responses.
An origin is the combination of scheme, host, and port. The URL path is not part of the origin. Thus https://app.example and https://api.example are different origins even when they belong to the same company. A successful navigation only proves that the browser could load the document. It does not prove that JavaScript on that document may read an API response.
The automated request can also differ from the one made during a manual test. Selenium may use another URL, cookies, authentication state, request method, custom headers, content type, redirect path, or application state. Diagnose the request rather than assuming that “the browser works” means both requests are equivalent.
#1 Best Overall
- Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (8GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- Mega Heat Sink - Black Anodized
Locate the failing request first
- Reproduce the error with DevTools open. Use the same Selenium flow that fails and read the Console and Network panels. Browser page code normally receives only a generic failure; the console supplies the specific CORS reason. As MDN puts it: “The only way to determine what specifically went wrong is to look at the browser’s console for details.”
- Identify the initiator. In Network, select the failed request and record the document origin, request URL, method, status, redirect chain, and the Initiator entry. Confirm whether the failure is an API call from page JavaScript rather than the initial document request.
- Compare request details. Record the
Originrequest header, cookies, authorization state, custom headers, and content type. Compare these with a successful manual run. A different scheme, host, or port is enough to change the CORS decision. - Inspect response headers. Look for
Access-Control-Allow-Originand verify that it exactly permits the page origin. A missing header or mismatch is a server configuration problem when the endpoint is intended to serve that page. Ensure that the response contains only one allow-origin header. - Separate preflight from the real request. If Network shows an
OPTIONSrequest, inspect it independently. The browser will not send the actual request until the preflight succeeds.
Do not rely on a Python exception alone. The browser console and Network panel show which origin, method, headers, credentials, and redirect caused the decision.
Understand preflight requests
Some cross-origin requests are “simple” and can be sent without a preflight. Others require the browser to ask permission first with OPTIONS. A preflight is commonly triggered by methods such as PUT, PATCH, or DELETE, by non-safelisted request headers (for example, a custom header), or by a content type outside the safelisted set.
The server’s OPTIONS response must authorize the actual page origin, method, and requested headers. Typical response fields are:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Access-Control-Allow-Origin: https://app.example:8443
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization, X-Client-Version
The names and values must match what the browser asked for. A preflight that returns a redirect, an authentication challenge, a 404, or missing CORS headers prevents the subsequent request from being sent.
Fix the API when you control it
Allow the exact origin
Configure an allowlist for the real page origin, including scheme and port. Do not use a broad wildcard merely to make the message disappear. If several front ends are supported, select an approved origin and return that value deliberately. When responses vary by origin, configure the appropriate cache variation (usually Vary: Origin) so a cached response is not reused for the wrong site.
Rank #2
- Includes Raspberry Pi 4 4GB Model B with 1.5GHz 64-bit quad-core CPU (4GB RAM)
- Includes Pre-Loaded 32GB EVO+ Micro SD Card (Class 10), USB MicroSD Card Reader
- CanaKit Premium High-Gloss Raspberry Pi 4 Case with Integrated Fan Mount, CanaKit Low Noise Bearing System Fan
- CanaKit 3.5A USB-C Raspberry Pi 4 Power Supply (US Plug) with Noise Filter, Set of Heat Sinks, Display Cable - 6 foot (Supports up to 4K60p)
- CanaKit USB-C PiSwitch (On/Off Power Switch for Raspberry Pi 4)
Handle OPTIONS before authentication and routing failures
Your server, reverse proxy, and framework must answer preflight requests with the required CORS headers. Make sure an authentication middleware does not reject OPTIONS before the CORS layer can respond. Test the exact requested method and headers shown in DevTools.
Configure credentials correctly
Cookies, HTTP authentication, and other credentials are a separate check. A credentialed cross-origin response must explicitly include:
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: * cannot be used for a credentialed request. Browser third-party-cookie policy can still block cookies even when the CORS headers are correct, so verify cookie attributes and browser policy separately.
Keep the policy narrow
Permit only the origins, methods, and headers the application needs. Reflecting any incoming Origin value without an allowlist can expose authenticated data. Review redirects as well: every cross-origin response that the browser must read needs a compatible policy.
When you do not control the API
No Selenium launch flag can legitimately grant your page access to a remote server that has not authorized its origin. Choose an architecture that the service owner supports:
| Approach | Browser CORS enforcement | Credentials | When it fits | Responsibilities |
|---|---|---|---|---|
| Page JavaScript through Selenium | Yes | Uses the browser session’s cookies and browser-visible credentials | The API explicitly allows the page origin and the result must be visible to page code | Maintain origin, preflight, cookie, and browser-policy compatibility |
| Authorized Python HTTP client | No browser CORS check | You supply the API’s supported credentials | A documented server-to-server API exists and browser rendering is unnecessary | Protect keys, reproduce required request semantics, and obey the service’s access rules |
| Controlled proxy | The browser calls your own origin | Your proxy handles upstream authentication | You are authorized to relay the data and need it in the page | Authentication, access control, rate limits, validation, logging, and data protection |
A proxy is not a permission bypass. Obtain authorization, restrict which upstream hosts can be contacted, and prevent an open-proxy or server-side request-forgery design.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Move an API call to Python only when that is the intended design
A Python HTTP request is not a request made by page JavaScript, so browser CORS enforcement does not apply. That can be appropriate for an authorized API integration, but it is not equivalent to browser interaction. You must reproduce the API’s documented authentication, parameters, method, and content negotiation, and you must not use it to evade access controls.
import requests
api_url = "https://api.example/data"
headers = {"Authorization": "Bearer YOUR_TOKEN", "Accept": "application/json"}
r = requests.get(api_url, headers=headers, timeout=30)
r.raise_for_status()
data = r.json()
print(data)
If the value is produced only after a user interaction in the page, keep Selenium for that interaction and pass the resulting, authorized value to your Python code. Do not assume that copying a browser cookie into a script is supported or safe.
Use Selenium without weakening browser security
Do not “fix” CORS with flags that disable web security or with an extension that suppresses checks. Those methods hide the server defect and create a test environment unlike a real user’s browser. They can expose data between origins and make a production failure appear solved.
Use a current, mutually compatible browser and Selenium binding. Selenium Manager can discover and cache drivers for common supported setups, but changing driver versions does not authorize a remote origin; it only addresses separate WebDriver compatibility problems.
Rank #4
- Broadcom BCM2711, quad-core Cortex-A72 (ARM v8) 64-bit SoC @ 1. 5GHz
- 2. 4 GHz and 5. 0 GHz IEEE 802. 11b/g/n/ac wireless LAN, Bluetooth 5. 0, BLE
- 2 × USB 3. 0 ports, 2 x USB 2. 0 Ports
- 2 × micro HDMI ports supproting up to 4Kp60 video resolution
- Micro SD card slot for loading operating system and data storage
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
# Keep normal browser security enabled.
driver = webdriver.Chrome(options=options)
try:
driver.get("https://app.example")
# Perform the same clicks and waits as a user.
finally:
driver.quit()
Why common “fixes” do not work
mode: "no-cors"
This produces an opaque response. Page JavaScript cannot inspect its status or body, so it does not solve a task that needs API data.
Changing the URL path
Moving from /v1/data to another path does not change the origin. Only scheme, host, or port changes do that, and changing them may point to a different service.
Removing a custom header blindly
That can avoid a preflight only if the API supports the resulting request and the header was not required. It does not create an allow-origin permission and may change authentication or application semantics.
Adding a browser extension
An extension may alter your local test but does not repair the server policy or represent real users. Remove it when validating the production flow.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsOr skip the browser setup
For jobs whose goal is a clean image or PDF of a URL rather than browser-side API data, ScreenshotNeo makes one authorized request to its screenshot API. It accepts cookie and consent banners, removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture, and reports whether a response was clean and billed. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Use the documented parameters and options at https://screenshotneo.com/docs/. A minimal call is:
Best Value
- Includes Raspberry Pi 5 16GB with 2.4Ghz 64-bit quad-core CPU (16GB RAM)
- Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
- CanaKit Turbine Black Case for the Raspberry Pi 5
- CanaKit Low Noise Bearing System Fan
- Mega Heat Sink - Black Anodized
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo supports full-page and element captures, device presets and custom viewports, retina scale, dark mode, PDF controls, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, easing migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to try it without a card.
Free tools Windows power users keep installed
One-click scans. No signup required.
Troubleshooting checklist
- Console says allow-origin is missing: add the exact page origin on the API response and verify the response you inspected is the one the browser received.
- Origin does not match: compare scheme, host, and port character for character; account for HTTP-to-HTTPS redirects and development ports.
- Preflight fails: allow
OPTIONS, the requested method, and every requested header; check proxy and authentication middleware. - Wildcard with credentials: replace
*with an explicit origin and returnAccess-Control-Allow-Credentials: truewhen credentials are genuinely required. - No request appears after OPTIONS: the preflight was rejected; fix its status and headers before debugging the application response.
- Manual browser test passes, Selenium fails: compare cookies, login state, URL, timing, request initiator, headers, and redirects rather than changing WebDriver security flags.
- Python request succeeds but the page still fails: the Python call bypassed browser CORS; configure the API or proxy so the browser request is authorized if page JavaScript needs the result.
- Driver errors are unrelated: resolve browser/driver discovery and compatibility separately; a driver update cannot add CORS permission.
FAQ
Is CORS a Selenium bug?
Usually no. Selenium controls the browser, while the browser enforces the page’s cross-origin policy.
Can a successful driver.get() prove the API is allowed?
No. Navigation and JavaScript’s permission to read a cross-origin response are different operations.
Should I add Access-Control-Allow-Origin: *?
Only for a deliberately public, non-credentialed endpoint. It is invalid for credentialed browser requests and is broader than most application policies require.
What information should I give an API owner?
Provide the page origin, failing URL, method, preflight details, requested headers, response status, redirect chain, and the exact console message, while removing secrets.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

