Bypass Cloudflare 1020: The Ultimate Guide for Web Scrapers (2026)
Cloudflare Error 1020 occurs when Cloudflare’s Web Application Firewall (WAF) blocks your IP address based on firewall rules triggered by suspicious request patterns, TLS fingerprints, or behavioral signals. Bypassing it requires a combination of residential proxies, realistic headers, request throttling, and stealth browser configurations. Simple IP rotation or User-Agent changes fail because Cloudflare analyzes multiple connection layers simultaneously.
This guide covers the real mechanics behind Error 1020 and provides tested methods to bypass it at scale
What Is Cloudflare Error 1020?
Cloudflare Error 1020 “Access Denied” occurs when a website’s firewall rules block your IP address due to suspicious or unauthorized activity. This error typically occurs when you violate security policies, such as attempting to access restricted content or sending excessive requests from your device.
During the WAF check, Cloudflare decides what to do: let you through to the original server, show a CAPTCHA, run some JavaScript to check if you’re legit, or block you completely. That’s when you see Error 1020.

Here are the main reasons this happens:
- Too many requests. If your script sends dozens of requests per second, Cloudflare sees it as an attack. Unlike error 1015, which is temporary, error 1020 results from higher request rates and can last much longer depending on Cloudflare’s threat assessment.
- Public proxies or VPN. IPs from data centers, free proxies, or VPNs are often blacklisted. Bots prefer them, so Cloudflare doesn’t.
- Non-human behavior. No JavaScript, weird headers, hidden links, or anything that looks “too automated” makes Cloudflare suspicious.
- Geo-blocking. Some sites just block entire countries.
Most online advice tells users to clear their browser cache and cookies, turn off VPN or proxy, disable extensions, restart their router, or switch networks. But if you’re a developer running a scraping script, that won’t help. You’ll need a different set of tools, and that’s what we’ll get into next.
How to Bypass Cloudflare Error 1020: 6 Effective Methods
To get around Cloudflare error 1020, here’s what you can try:
- Proxy Rotation. Residential proxies with automatic rotation prevent IP-based blocking.
- Header Normalization. Match browser header sets exactly, including fetch metadata and cipher suites.
- Request Throttling. Random delays between requests mimic human browsing patterns.
- Stealth Browsers. Undetected ChromeDriver or Puppeteer stealth mode hides automation signals.
- Automated Captcha Solving. Services handle challenge pages when Cloudflare requires manual verification.
- Web Scraping APIs. Managed services handle the entire bypass stack including proxies and fingerprinting.
Now let’s break each of these methods down a bit more.
Use Residential Proxies with Rotation
When scraping data, it’s always better to use proxies. They help protect your real IP address. We’ve already written a separate article about what proxies are and the different types, so we won’t go into that here. One key tip: opt for residential proxies from trusted providers. Free ones are usually useless and sometimes even risky.
To use proxies in Python, you can go with the requests library:
import requests
proxies = {
"http": "http://username:[email protected]:8080",
"https": "http://username:[email protected]:8080"
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)Besides just using proxies, it’s better to rotate them, basically, switch them up constantly. That helps to avoid getting blocked. One way is to pick random proxies from a list for each request:
import random
proxies = [
"http://user1:[email protected]:8000",
"http://user2:[email protected]:8000",
"http://user3:[email protected]:8000"
]
proxy = random.choice(proxies)Or you can use rotating proxies. In that case, your proxy provider handles the rotation for you.
Set Realistic User-Agent and Headers
Next, you need to set the request headers. Sure, the User-Agent is one of the key ones, but it’s not the only sign that a script is run by a bot. Other important headers include:
| Header | Purpose |
|---|---|
| User-Agent | Identifies the browser |
| Accept | Tells the server what content types are supported |
| Accept-Language | Language preferences (should match typical browser settings). |
| Referer | Indicates where the request came from (bots often skip it). |
| Connection | Normally keep-alive in browsers. |
| Sec-Fetch-Site | Part of browser fetch metadata (e.g., none, same-origin, etc.). |
| Sec-Fetch-Mode | Typically navigate, cors, etc. |
| Sec-Fetch-Dest | Indicates the destination type (document, script, etc.). |
| Sec-Fetch-User | Present only in top-level navigation with user action (?1). |
To add headers to a request, pass them like this:
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Connection": "keep-alive"
}
response = requests.get("https://httpbin.org/headers", headers=headers)If you’re looking for a list of the latest User Agents and want to learn about them, we’ve got a separate post just for that.
Now, if you’ve already changed your headers and hidden the fact that you’re using a bot, but you’re still hitting a 1020 error, it might be because Cloudflare has already identified you before a single header was even sent. This happens at the connection level, through what is known as a TLS fingerprint. In that case, try using tls-client.
from tls_client import Session
session = Session(client_identifier="chrome_136")
resp = session.get("https://example.com")
print(resp.status_code, resp.text[:100])When your browser or script sets up an HTTPS connection, it shares a set of encryption settings (cipher suites, extensions). Cloudflare uses this info to create a short fingerprint called JA3. If the first HTTP request follows right after TLS, the order of its headers is used to make another fingerprint — JA4.
Libraries, like requests, use the default OpenSSL stack or built-in TLS modules, where cipher suites and extension order are different from a browser’s. More advanced libraries, like tls-client (which we used earlier), wrap browser-style TLS or tweak OpenSSL settings so your request looks more like a real browser in terms of JA3.
Add Random Delays Between Requests
Another small trick to make your script feel more human and reduce the chance of being blocked by Cloudflare 1020 is to add small delays between your requests. Even better, make them random:
import time
delay = random.uniform(1.5, 4.0)
time.sleep(delay)That way your scraping looks less suspicious.
Use a Stealth Headless Browser (Selenium, Puppeteer)
If you’re still getting hit with Cloudflare 1020, try using a headless browser. Keep using proxies and User-Agents, but now run the actual requests through something like Selenium:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument(f'--proxy-server=http://user1:[email protected]:8000')
options.add_argument(f'user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36')
driver = webdriver.Chrome(options=options)
driver.get("https://httpbin.org/ip")
driver.quit()You can also run the browser in headless mode, allowing it to operate in the background. If you prefer coding in NodeJS, Puppeteer or Playwright might be a better fit.
Hide Headless Browser Signals
Cloudflare is usually pretty good at spotting headless browsers. The thing is, standard WebDrivers give off some clear signals that make them easy to detect:
navigator.webdriver = true. This JavaScript property is automatically set when a page is loaded through WebDriver. In a real browser, it’s either undefined or false.

- Missing chrome.runtime. Chrome extensions use window.chrome.runtime to talk to the browser. In headless mode (especially with plain WebDriver), this object might be missing or incomplete, which can cause errors when accessed.
- Weird
window.outerWidth/outerHeightvalues. Headless browsers often set these equal toinnerWidth/innerHeight, or default to something fixed like 800×600. On a real device, these values are usually larger and don’t match inner dimensions. - No plugins. Real users almost always have at least one plugin. If navigator.plugins.length is 0, that’s a red flag.
To get around this, you can use stealth plugins or libraries that offer “undetectable” modes. These tools hide navigator.webdriver, fake real plugins and MIME types, add window.chrome objects like in real Chrome, and smooth out other headless quirks.
If you’re using Python, try undetected-chromedriver or SeleniumBase (it supports UC-mode, which is built on top of undetected-chromedriver):
from seleniumbase import SB
with SB(uc=True, headless=False) as sb:
url = "https://httpbin.org/headers"
sb.uc_open_with_reconnect(url, 3)
html = sb.get_page_source()This setup usually slips past Cloudflare without triggering a 1020 error. If you’re using Node.js, check out puppeteer-extra-plugin-stealth. It not only patches JavaScript fingerprints but also tweaks TLS settings to better mimic a real browser.
Integrate Automated Captcha Solvers
Sometimes stealth is not enough. The WAF may force a challenge (specifically Cloudflare Turnstile) on every request regardless of your fingerprint. In these edge cases, you need a dedicated captcha solver service to maintain automation.
Services like 2Captcha, CapSolver, or Anti-Captcha provide an API to bypass these barriers. You extract the sitekey and the page URL from the target website and send them to the solver’s API. The service returns a valid token which you inject into the page’s DOM or request payload. This allows the session to proceed as if a human solved the puzzle. While this adds latency to the request pipeline, a robust captcha solver is often the only way to access high-security pages without manual intervention.
Use a Web Scraping API
Using undetectable browsers, while effective, can be resource-intensive. Add proxy rotation, CAPTCHA-solving services, and the fact that Cloudflare doesn’t always block instantly but runs multiple checks first… and the costs can pile up quickly.
That’s why, in many cases, using a scraping API or a dedicated service might actually be a more practical solution. These tools are built specifically to bypass protections like Cloudflare, and they constantly evolve to stay ahead. Plus, you don’t have to worry about proxies, headless browsers, or maintaining the whole stack yourself.
One example is HasData’s web scraping API, which takes care of the entire scraping pipeline, from proxy management to CAPTCHA solving, with 99.9% uptime and fast response times. All you need is a Hasdata API key (you get it after signing up). Then set your parameters and get the content you need:
import requests
import json
api_key = "YOUR-API-KEY"
url = "https://api.hasdata.com/scrape/web"
payload = json.dumps({
"url": "https://example.com",
"proxyType": "datacenter",
"proxyCountry": "US",
"screenshot": True,
"jsRendering": True
})
headers = {
'Content-Type': 'application/json',
'x-api-key': api_key
}
response = requests.request("POST", url, headers=headers, data=payload)Check the docs for all the available options. And if there’s an API for the site you’re scraping (like Google SERP, Zillow, etc.), use it, it’ll save you time.
Full Code Example: Undetectable Headless Browser, Custom Headers, and Delays
Here’s a full example combining undetectable headless browser mode, custom headers, proxies, and request delays:
from seleniumbase import SB
import random
import time
proxies = [
"http://111.111.111.111:8000",
"http://222.222.222.222:8000"
]
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
]
proxy = random.choice(proxies)
ua = random.choice(user_agents)
with SB(uc=True, headless=False, proxy=proxy, user_agent=ua) as sb:
url = "https://httpbin.org/headers"
sb.uc_open_with_reconnect(url, 3)
time.sleep(random.uniform(2.5, 4.5))
html = sb.get_page_source()It also picks random proxies and User-Agents. You can change the script however you like.
Cloudflare Bypass Test Results and Metrics
To see if some of these methods actually work, we tested them on three websites with different levels of Cloudflare protection:
- Site A. Basic Cloudflare protection, checks for bots and scripts, and sometimes throws a captcha.
- Site B. Active blocking is on for suspicious requests and repeated connections.
- Site C. Strict protection with advanced bot detection.
You can set up your own site and configure Cloudflare however you want, or build your own list and try the tests yourself. When you visit a Cloudflare-protected site, check the response headers — you’ll find a cf-ray parameter, which is the Cloudflare Ray ID.

To minimize randomness, each method was tested with 1,000 consecutive requests to each site. All tests were conducted on the same PC under identical conditions.
A request was marked as “successful” if it returned an HTTP status code 200 and the response contained the expected element on the page. Any other status code (e.g., 403, 1020) or a response requiring CAPTCHA was marked as “failure.”
Here’s what we got after trying different methods, both separately and combined:
| Technique | Site A. Success Rate (%) | Site B. Success Rate (%) | Site C. Success Rate (%) | Avg. Response Time (s) | Avg. CPU (%) | Avg. Mem (%) |
|---|---|---|---|---|---|---|
| No Proxy, No Headless | 63 | 42 | 41 | 0.44 | 13.25 | 54.4 |
| Datacenter Proxy Only | 55 | 30 | 43 | 2.32 | 15.11 | 56.1 |
| Residential Proxy + Headers | 71 | 68 | 64 | 1.36 | 11.3 | 53.6 |
| SeleniumBase + UC mode | 91 | 89 | 77 | 5.09 | 78.16 | 76.1 |
| API-Based (HasData API) | 99 | 99 | 97 | 4.38 | 14.4 | 56.2 |
Without running scripts, CPU usage stays around 7%, and memory around 37%.
In general, these tests give you a rough idea of what works better against Cloudflare and what doesn’t. However, in real-world scraping scenarios, proxies are usually rotated or replaced as soon as they’re blocked, unlike in these tests.
Also, a lot depends on how strict the site’s Cloudflare settings are. Some pages hit you with a challenge or block (error 1020) immediately. Others let you through a few times before getting serious.
Conclusion: Which Method Should You Choose?
Ultimately, the path you choose depends on the scale of your project and the resources available to your team. For smaller projects or to learn the intricacies of web security, building and maintaining a stealthy browser solution is an invaluable exercise.
However, for large-scale, mission-critical data extraction where reliability and speed are paramount, the resource overhead and constant maintenance of a DIY solution often make a dedicated web scraping API the more efficient and cost-effective choice.
The constant evolution of Cloudflare means the game is never truly “won.” You either commit to playing it continuously or you delegate it to a team that does.


