# 403 Forbidden w Web Scraping: Drabina Napraw, Która Naprawdę Działa

403 to nie problem z uprawnieniami — to problem z wykryciem. Cztery szczeble oddzielają zablokowany skrypt od 200, a większość ludzi zatrzymuje się na pierwszym.

Błąd 403 forbidden w web scrapingu prawie nigdy nie oznacza tego, co mówi kod statusu. HTTP 403 jest zdefiniowany jako serwer rozumiejący twoje żądanie i odmawiający jego autoryzacji — ale kiedy trafia na scraper, rzadko ma to coś wspólnego z uprawnieniami lub brakującym logowaniem. Oznacza to, że strona spojrzała na twoje żądanie, zdecydowała, że wysłała je maszyna, i zamknęła drzwi. To mówi ci, co zmienić: nie twoje dane uwierzytelniające, ale kształt twojego ruchu. Poniżej znajduje się drabina napraw, od najtańszego szczebla, z kontrolami, które identyfikują, na którym szczeblu jesteś zablokowany.

## 403 vs 401 vs 429: co każdy z nich ci mówi

Postaw właściwą diagnozę, zanim napiszesz kod. **401 Unauthorized** prosi o dane uwierzytelniające — dostarczenie ich rozwiązuje problem. **403 Forbidden** odmawia niezależnie od danych uwierzytelniających, więc logowanie nic nie zmienia, jeśli wykrycie bota go wywołało. **429 Too Many Requests** dotyczy ilości i sam się rozwiązuje, gdy okno się resetuje; 403 dotyczy tożsamości i trwa, dopóki nie zmienisz wyglądu swojego żądania. Jeśli twój scraper otrzymuje 429 zamiast 403, lekarstwem jest tempo, a nie maskowanie — omawiamy to w [naprawie 429 too many requests](https://quantumproxies.io/blog/fix-429-too-many-requests-scraping).

## Zdiagnozuj w 60 sekund, zanim zmienisz jakikolwiek kod

Trzy polecenia powiedzą ci prawie wszystko. Uruchom czyste żądanie, uruchom je ponownie tylko z zamienionym User-Agent przeglądarki, a następnie przeczytaj treść odpowiedzi — powód blokady jest zwykle w niej napisany.

```bash
# 1. Bare request: what does the target give a naked client?
curl -sS -o /dev/null -w '%{http_code}\n' https://target.example/page

# 2. Same request, browser User-Agent only
curl -sS -o /dev/null -w '%{http_code}\n' \
  -A 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' \
  https://target.example/page

# 3. Read the body and the response headers - the reason is in there
curl -sS -D - https://target.example/page | head -c 600
```

Interpretuj to tak. Jeśli krok 2 zwraca 200, cały problem to nagłówki i jesteś gotowy na szczeblu 1. Jeśli treść wspomina o Cloudflare, Ray ID lub Error 1020, jesteś za regułą WAF — zobacz [Cloudflare error 1020](https://quantumproxies.io/blog/cloudflare-error-1020-access-denied). Zwykła strona Forbidden Apache lub nginx zwykle oznacza moduł serwera, taki jak mod_security, który blokuje znane User-Agents botów od dawna przed istnieniem nowoczesnego zarządzania botami. A jeśli przeglądarka na tym samym komputerze ładuje stronę, a twój klient nie, przejdź przez [curl 403 but the browser works](https://quantumproxies.io/blog/curl-403-but-browser-works).

![Zablokowany skrypt wspina się po czteroszczeblowej drabinie poprawek: nagłówki przeglądarki, potem odcisk palca TLS klasy przeglądarki z curl_cffi, potem rezydencyjne IP wyjściowe, a potem renderowanie dla wyzwań JavaScript. Karta mówi: testuj ponownie po każdej pojedynczej zmianie, nigdy nie zmieniaj dwóch rzeczy naraz i ponawiaj 403 najwyżej raz.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335150/blog/generated/qp-b9-011-diag1.png)
*Wspinaj się po jednym szczeblu, przetestuj ponownie, zatrzymaj się, gdy tylko otrzymasz 200. Renderowanie kosztuje najwięcej i naprawia najmniej.*

## Szczebel 1: przestań się ujawniać w nagłówkach

`urllib` Pythona identyfikuje się jako coś w rodzaju `python-urllib/3.3.0`; `requests` wysyła `python-requests/2.x`. Te ciągi to wyznanie, a najbardziej głosowana odpowiedź na kanonicznym wątku Stack Overflow o 403 w Python scrapingu jest po prostu: wyślij User-Agent przeglądarki zamiast tego. To wciąż działa na wielu stronach. Ale od czasu napisania tej odpowiedzi zmieniły się dwie rzeczy. Po pierwsze, czysty `Mozilla/5.0` jest teraz sam w sobie flagą — komentatorzy na tym samym wątku zgłaszają, że strony blokują go całkowicie, ponieważ żadna prawdziwa przeglądarka nie wysyła dwuelementowego UA. Po drugie, nowoczesne serwery porównują cały zestaw nagłówków, a nie jedno pole.

Wyślij spójny zestaw: aktualny UA przeglądarki, pasujący łańcuch `Accept`, język i nagłówki metadanych `Sec-Fetch-*`, które Chromium dodaje do każdej nawigacji. Kolejność nagłówków ma również znaczenie na bardziej rygorystycznych celach — użyj uporządkowanego mapowania i umieść je w kolejności używanej przez przeglądarkę.

```python
import requests

HEADERS = {
    "User-Agent": (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
        "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
    ),
    "Accept": (
        "text/html,application/xhtml+xml,application/xml;q=0.9,"
        "image/avif,image/webp,*/*;q=0.8"
    ),
    "Accept-Language": "en-GB,en;q=0.9",
    "Accept-Encoding": "gzip, deflate",   # add 'br' only if brotli is installed
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
    "Connection": "keep-alive",
}

with requests.Session() as s:
    s.headers.update(HEADERS)
    r = s.get("https://target.example/page", timeout=20)
    print(r.status_code, len(r.content))
```

Jedna pułapka spójności łapie prawie wszystkich: UA twierdzący, że jest to US Chrome desktop, sparowany z `Accept-Language: de-DE` i wyjściowym IP w Brazylii, to niezgodność, którą zauważy każdy przyzwoity system. Utrzymuj, aby User-Agent, język i geografia IP opowiadały tę samą historię.

Niektóre 403 na tym szczeblu są jeszcze prostsze: brakujący `Referer`. Serwery, które serwują zasób tylko wtedy, gdy żądanie wygląda, jakby pochodziło z ich własnej strony, zwrócą 403 na bezpośrednie trafienie i 200, gdy tylko dodasz adres URL odsyłający. To klasyka i kosztuje jeden nagłówek do przetestowania.

## Szczebel 2: nagłówki TLS nie mogą naprawić

Jeśli doskonałe nagłówki wciąż zwracają 403, blokada nastąpiła, zanim twoje nagłówki zostały nawet odczytane. Każdy klient HTTPS ogłasza swoje zestawy szyfrów, rozszerzenia, krzywe eliptyczne i ALPN w TLS ClientHello, a ta kombinacja haszuje się w odcisk palca JA3 lub JA4. `requests` Pythona, `net/http` Go i standardowy `curl` mają każdy charakterystyczny, który żaden dostawca anty-botów nie ma problemu odróżnić od Chrome. Twierdzenie, że jesteś Chrome 131 w nagłówku, podczas gdy podajesz się jak OpenSSL, to najgłośniejsza sprzeczność, jaką może zrobić scraper.

Naprawą jest klient, który udaje prawdziwą przeglądarkę na warstwie TLS. W Pythonie to `curl_cffi`, powiązanie z załatanym libcurl, które odtwarza przeglądarkowe ClientHellos:

```python
# pip install curl_cffi
from curl_cffi import requests as cffi

proxy = "http://USER:PASS@residentialpremium.quantumproxies.io:8080"

r = cffi.get(
    "https://target.example/page",
    impersonate="chrome",              # Chrome JA3/JA4 + HTTP/2 settings
    proxies={"http": proxy, "https": proxy},
    timeout=20,
)
print(r.status_code)
```

Node ma odpowiedniki zbudowane na tych samych załatanych stosach TLS. Jeśli chcesz poznać pełną mechanikę, dlaczego ta pojedyncza zmiana zamienia 403 na 200 na chronionych stronach, przeczytaj [jak odcisk palca JA3/JA4 ujawnia twój scraper](https://quantumproxies.io/blog/tls-ja3-fingerprinting-scraping).

## Szczebel 3: IP to wiadomość

Nagłówki i TLS opisują klienta. IP opisuje, kto pyta, i jest mocno ważone. Adresy w zakresach hostingowych i chmurowych są publikowane, łatwe do zmapowania przez ASN i mają niższy wynik zaufania, zanim zostanie zbadany choćby jeden bajt twojego żądania — dlatego scraper na VPS dostaje 403, które ten sam kod na połączeniu domowym przechodzi bez problemu. Adresy rezydencyjne należą do dostawców internetu dla konsumentów i są traktowane jak ludzie; IP operatorów komórkowych siedzą za CGNAT z tysiącami prawdziwych subskrybentów każdy, co czyni je najtrudniejszymi do zablokowania hurtowo.

Więc szczebel 3 to zamiana, nie przepisanie: wyślij to samo dobrze uformowane żądanie z wyjścia rezydencyjnego. QuantumProxies obsługuje ponad 90 milionów rezydencyjnych IP w ponad 200 krajach z rotacją na żądanie lub sesjami stałymi, HTTP i SOCKS5 na każdym planie, i rozliczaniem za GB — jedna linia konfiguracji zmienia ASN, który widzi twój cel. Już jesteś na rezydencyjnym i nadal zablokowany? Sprawdź reputację puli za pomocą naszego darmowego [sprawdzacza jakości IP](https://quantumproxies.io/ip-checker): wszystko, co ma wynik powyżej 75, jest spalone i będzie zbierać 403, niezależnie od tego, jak dobre są twoje nagłówki.

[Pomiń drabinę: pobierz dowolną stronę za pomocą Scraper API](https://quantumproxies.io/scraper-api)

![Tabela porównuje sygnały żądania jeden po drugim, scraper kontra prawdziwa przeglądarka, z wyróżnioną kolumną przeglądarki. Karta wymienia niezgodności, które kończą się 403: User-Agent python-requests, UA Chrome na handshake OpenSSL, amerykański Chrome z językiem de-DE i brazylijskim IP wyjściowym. Rozwiązaniem jest jedna spójna historia.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335150/blog/generated/qp-b9-011-diag2.png)
*Systemy anty-botowe testują spójność. Jeden niezgodny sygnał na tej liście wystarczy na 403.*

## Szczebel 4: renderowanie i wyzwania

Ostatni szczebel jest najdroższy. Niektóre 403 to widoczna połowa wyzwania JavaScript: serwer wysyła mały skrypt, oczekuje odpowiedzi w ciągu kilku sekund i odmawia wszystkiego, co nie może go wykonać. Żaden zestaw nagłówków i żadne IP tego nie naprawią, ponieważ test polega na tym, czy możesz uruchomić kod. Opcje w porządku rosnących kosztów: przeglądarka bezgłowa z zestawem łatek stealth, zarządzana pula przeglądarek lub API scrapingu, które renderuje na żądanie. Renderowanie kosztuje wiele razy więcej za stronę niż zwykłe żądanie HTTP, więc eskaluj tylko dla URL-i, które naprawdę tego potrzebują.

QuantumProxies [Scraper API](https://quantumproxies.io/scraper-api) łączy szczeble 2 do 4 w jedno żądanie: TLS na poziomie przeglądarki, wyjścia rezydencyjne, renderowanie JavaScript, gdy strona tego potrzebuje, oraz markdown, JSON lub surowe HTML z powrotem. To uczciwa wymiana — przestajesz utrzymywać drabinę i płacisz za każdą udaną stronę.

## Praca z drabiną w praktyce

- Przetestuj ponownie po każdej zmianie. Dwie zmiany naraz i niczego się nie nauczysz o tym, która zadziałała.
- Buforuj surowe ciało 403 podczas rozwoju. Strony blokujące zawierają Ray ID, nazwy reguł i odciski palców dostawcy, które nazywają twojego przeciwnika.
- Traktuj 403 przy pierwszym żądaniu jako problem tożsamości; 403, które pojawia się po 200 dobrych stronach, to problem reputacji lub tempa.
- Ponów próbę 403 najwyżej raz i tylko po zmianie czegoś. Uderzanie tym samym żądaniem z tego samego IP zamienia miękką blokadę w twardą.
- Jeśli istnieje publiczne API lub kanał dla tych samych danych, użyj go. Jest tańsze niż każdy szczebel powyżej i nigdy nie psuje się przy zmianie projektu.

Zapobieganie banom to pokrewna dyscyplina: gdy masz 200, utrzymanie go to kwestia tempa, higieny sesji i zdrowia puli, a nie maskowania.

## Najczęściej zadawane pytania

### Co powoduje błąd 403 forbidden w web scrapingu?

Wykrycie, w prawie każdym przypadku. Zwykłe wyzwalacze to domyślny User-Agent biblioteki, niekompletny lub sprzeczny zestaw nagłówków, IP z centrum danych o złej reputacji, tempo żądań, które jest zbyt regularne, lub odcisk palca TLS, który nie pasuje do przeglądarki, którą twierdzisz, że jesteś. Prawdziwe błędy uprawnień istnieją, ale zwracają 403 również przeglądarkom — przetestuj w jednej, zanim założysz.

### Jak obejść błąd 403 forbidden w Pythonie?

Pracuj z drabiną. Dodaj pełny zestaw nagłówków przeglądarki do `requests.Session`; jeśli to się nie uda, przełącz się na klienta, który udaje TLS przeglądarki, takiego jak `curl_cffi`; jeśli to się nie uda, przejdź przez proxy rezydencyjne; jeśli strona wysyła wyzwanie JavaScript, renderuj je lub użyj API scrapingu. Eskaluj tylko wtedy, gdy tańszy szczebel został faktycznie przetestowany.

### Dlaczego httpx zwraca 403, gdy moja przeglądarka tego nie robi?

Z tego samego powodu co `requests`: httpx wysyła minimalny zestaw nagłówków i odcisk palca TLS Pythona. Skopiuj dokładne żądanie przeglądarki z DevTools, odtwórz je z httpx, a 403 zwykle znika — co mówi ci, że różnica była w nagłówkach. Jeśli trwa z identycznymi nagłówkami, blokada jest na warstwie TLS lub IP.

### Jak naprawić 403 forbidden w Scrapy?

Ustaw realistyczne `DEFAULT_REQUEST_HEADERS` plus `USER_AGENT`, trzymaj `ROBOTSTXT_OBEY` uczciwie o tym, co możesz pobrać, włącz `AUTOTHROTTLE_ENABLED` i kieruj żądania przez rotacyjne proxy middleware. Scrapy domyślnie nie ponawia 403 — dodaj go do `RETRY_HTTP_CODES` tylko wtedy, gdy rotujesz IP między próbami.

403 to informacja, nie ściana. Mówi ci, który z czterech sygnałów cię zdradził, a każdy szczebel drabiny kosztuje więcej niż ten poniżej. Zacznij od najtańszego, testuj po każdej zmianie i przestań się wspinać, gdy tylko kod statusu zmieni się na 200.

[Zdobądź proxy rezydencyjne, które przejdą szczebel 3](https://quantumproxies.io/residential-proxies)
