What a 403 Forbidden error means for visitors and site owners
A 403 is the server saying "I know what you asked for, and you can't have it." The page usually exists. Access is refused because of who is asking, where the request comes from, or how the server is configured. Logging in again often doesn't help, which is what separates it from a 401.
If you're visiting a site:
- Check the URL. A link to a folder instead of a page (ending in
/) returns 403 on many servers when directory listing is turned off. - Reload, then clear the site's cookies and cache. A stale session cookie can keep you locked out.
- Turn off your VPN or proxy and try again. Many sites refuse whole IP ranges that belong to VPNs, data centers, or other countries.
- Log in if the page belongs to an account, and check that your account has access to it.
- If nothing works, the block is on the site's side. Contact the site owner and mention the time and the URL.
If you run the site:
- Check file and folder permissions. On Linux web servers, files usually need
644and folders755, readable by the web server user. - Make sure the folder has an index file (
index.htmlorindex.php), or the server refuses to list it. - Look for
denyrules in.htaccessor your Nginx config, and for IP allowlists that leave out your visitors. - Check your firewall, CDN, or WAF logs (Cloudflare, Sucuri, Wordfence). A security rule or country block is the most common cause of 403s for real visitors.
Why 403s happen in scraping
How to diagnose a 403
cf-ray, x-amzn-waf-action, server: AkamaiGHost, or x-datadome to see which vendor it is. Then check what you're sending: is the User-Agent realistic? Is Accept-Language present? Does your TLS fingerprint match the browser you claim to be? Finally, try the same URL through a residential proxy in the target's main country — if that works, the block was based on your IP; if it still fails, the block is based on your fingerprint.How to recover from a 403
Fixing a 403 in Python requests
Work through four layers in order — most 403s are solved by the first two:
- Send a realistic
User-Agent. The defaultpython-requests/2.xstring is the single most common cause of a 403 that works fine in a browser. - Send the full browser header set —
Accept,Accept-Language,Accept-Encoding, andReferer. A request with only aUser-Agentstill looks nothing like a real browser. - Persist cookies with a
requests.Session(). Many sites set a cookie on first load and 403 any request that arrives without it. - If 403s survive all of the above, the site is fingerprinting your TLS/HTTP2 handshake (typical of Cloudflare and DataDome). Plain
requestscan't change that — switch to curl_cffi withimpersonate="chrome", a headless browser, or a managed scraping API.
The code example below shows the headers-plus-session approach and the curl_cffi fallback side by side.
