curl zwraca 403, ale przeglądarka działa: znajdź brakujący element
Przeglądarka ładuje stronę. curl zwraca 403. Różnica między tymi dwoma żądaniami jest zawsze skończona i zawsze możliwa do znalezienia — oto jak ją podzielić w pięć minut.
Wklejasz URL do Chrome i strona się ładuje. Wklejasz ten sam URL do curl i otrzymujesz 403 Forbidden. Nic się nie zmieniło w zasobie między tymi dwoma sekundami, więc różnica leży całkowicie w żądaniu — a żądanie to skończona, możliwa do zbadania rzecz. Ten przewodnik to procedura podziału: odtwórz dokładnie to, co wysłała przeglądarka, a następnie usuwaj elementy, aż 403 powróci. Cokolwiek usunąłeś ostatnio, to jest twoja odpowiedź. Podejrzani, w kolejności, w jakiej zwykle są winni, to User-Agent, Referer, cookies, TLS fingerprint i JavaScript.
Krok 0: udowodnij, że żądania naprawdę się różnią
Zanim zaczniesz teoretyzować, zobacz, co curl faktycznie wysyła. Dzięki -v widzisz linię żądania, każdy nagłówek i handshake TLS. Domyślne żądanie curl jest zaskakująco skromne — zazwyczaj Host, User-Agent: curl/8.x i Accept: */*. Przeglądarka wysyła kilkanaście więcej.
# what you send, what you get back, and the TLS details
curl -v -o /dev/null https://target.example/page
# just the response headers, quickly
curl -sS -o /dev/null -D - https://target.example/page
Przeczytaj nagłówki odpowiedzi tak uważnie, jak linię statusu. Jeden z nich rozstrzyga pytanie od razu w najczęstszym przypadku: Vary: User-Agent oznacza, że serwer celowo serwuje różne odpowiedzi w zależności od tego, kim twierdzisz, że jesteś. W dobrze udokumentowanym przypadku na Stack Overflow, curl -f przeciwko zwykłemu hostowi Apache 2.4.38 zwrócił 403, podczas gdy wget pobrał identyczny plik z 200 — a udana odpowiedź zawierała dokładnie ten nagłówek Vary: User-Agent. Przekazanie -A 'Wget/1.21.2' do curl naprawiło to natychmiast. Właściciel witryny zablokował user agent curl po nadużyciach; nic innego w żądaniu nie miało znaczenia.
Podczas czytania wyjścia: curl: (22) The requested URL returned error: 403 nie jest osobnym problemem. Kod wyjścia 22 to to, co -f/--fail robi z każdym błędem HTTP — flaga tłumi ciało i powoduje niepowodzenie komendy. Usuń tymczasowo -f, aby móc faktycznie przeczytać stronę blokady, która zazwyczaj podaje nazwę systemu, który cię zatrzymał.
Krok 1: Kopiuj jako cURL, 30-sekundowa odpowiedź
Obie główne przeglądarki mogą przekazać ci dokładne żądanie, które właśnie wykonały. Otwórz DevTools, przejdź do zakładki Network, kliknij prawym przyciskiem myszy żądanie i wybierz "Copy as cURL". Chrome wprowadziło to od wersji 26, a Firefox od 31, a wyjście zawiera każdy nagłówek, każde ciasteczko i referer. Wklej to do swojego terminala: jeśli zwróci 200, twój problem leży definitywnie w kształcie żądania, a krok 2 znajdzie, która część.
Jedna pułapka marnuje tu dużo czasu. Jeśli URL przekierowuje, panel Network czyści się przy nawigacji i kopiujesz złe żądanie. Zaznacz "Preserve log" w Chrome lub "Persistent Logs" w Firefox najpierw, aby móc zobaczyć zarówno żądanie, które przekierowało, jak i to, które ostatecznie serwowało treść. Łańcuchy przekierowań mają znaczenie: w dobrze znanym wątku na Unix Stack Exchange serwer sprawdzał Referer, a następnie przeskakiwał przez 302 do lokalizacji, która niczego nie sprawdzała — co sprawiało, że niepowodzenie wyglądało na losowe, dopóki cały łańcuch nie był widoczny.

Krok 2: podziel nagłówki
Zacznij od działającej komendy "Copy as cURL" i usuwaj nagłówki jeden po drugim, uruchamiając ponownie po każdym usunięciu. Pierwsze usunięcie, które przywraca 403, wskazuje twojego winowajcę. W praktyce jest to prawie zawsze jeden z czterech.
curl -sS -o /dev/null -w '%{http_code}\n' \
-A 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' \
-H 'Accept-Language: en-GB,en;q=0.9' \
-e 'https://target.example/' \
-b 'session=abc123; consent=1' \
-L \
'https://target.example/page'
- User-Agent (
-A) — zablokowany bezpośrednio, lub serwer rozgałęzia się na jego podstawie. Przetestuj aktualny ciąg Chrome, potem wget, potem nonsensowny; wzór wyników powie ci, czy trafiasz na czarną listę, czy na białą listę. - Referer (
-e) — zasoby i linki do pobrania często zwracają 403, chyba że żądanie wygląda, jakby pochodziło z własnej strony witryny. Nagłówek jest opcjonalny według specyfikacji, co jest dokładnie powodem, dla którego ludzie o nim zapominają. - Cookies (
-b) — ciasteczko zgody, sesji lub anty-bot ustawione na wcześniejszej stronie. Potwierdź to w kilka sekund: otwórz URL w oknie prywatnym. Jeśli przeglądarka również zwraca 403, ciasteczka są twoją odpowiedzią. - Authorization (
-u, lub nagłówek bearer) — podpisane lub tokenizowane URL-e często zwracają 403, gdy są kopiowane poza kontekst, ponieważ token był powiązany z sesją lub już wygasł.
Dwa zamykające szczegóły dla tego szczebla. Cytuj URL: ciąg zapytania zawierający & lub token dostępu zostaje zniekształcony przez twoją powłokę inaczej, a wynikowe 403 nie ma nic wspólnego z serwerem. A jeśli debugujesz z PHP lub Node zamiast z powłoki, odtwórz ten sam zestaw nagłówków tam — domyślne ustawienia libcurl wewnątrz PHP różnią się od narzędzia wiersza poleceń, co jest powodem, dla którego identyczne żądanie może przejść w terminalu i nie powieść się w kodzie. Nasze przepisy curl z proxy obejmują pełną składnię flagi.
Krok 3: gdy identyczne nagłówki nadal zwracają 403
Jeśli kopia bajt po bajcie nagłówków przeglądarki nadal nie działa, decyzja została podjęta przed przetworzeniem twoich nagłówków. Pod nimi znajdują się dwie warstwy.
TLS fingerprint. Twój ClientHello — zestawy szyfrów, rozszerzenia, preferencje krzywych, ALPN, plus ramka ustawień HTTP/2, która następuje — haszuje się do wartości JA3 lub JA4. curl zbudowany na OpenSSL produkuje taki, którego żadna przeglądarka nigdy nie produkuje, a systemy anty-bot porównują go z twoim deklarowanym User-Agent. Twierdzenie, że jesteś Chrome, podczas gdy handshake wygląda jak OpenSSL, to sprzeczność, którą są zbudowane, aby wychwycić. Rozwiązaniem jest klient, który odtwarza handshakes przeglądarki: curl-impersonate w wierszu poleceń, lub curl_cffi z Python.
# pip install curl_cffi
from curl_cffi import requests
proxy = "http://USER:PASS@gate.quantumproxies.io:8000"
r = requests.get(
"https://target.example/page",
impersonate="chrome", # browser ClientHello + HTTP/2 settings
proxies={"http": proxy, "https": proxy},
timeout=20,
)
print(r.status_code, r.headers.get("content-type"))
Twój IP. Przeglądarka, która działa, zazwyczaj jest na twoim domowym połączeniu, podczas gdy curl działa na VPS. Hosting ASNs są publikowane i oceniane, więc to samo żądanie z adresu rezydencjalnego jest oceniane inaczej, zanim zostanie nawet przeczytane. Zmiana wyjścia to jednolinijkowa zmiana z residential proxies — ponad 90 milionów IP w ponad 200 krajach, HTTP i SOCKS5 w każdym planie — i jest to najszybszy sposób, aby wykluczyć sieć. Mechanika warstwy fingerprint jest opisana w JA3/JA4 TLS fingerprinting.
Wyklucz sieć za pomocą residential proxies

Krok 4: strona potrzebuje przeglądarki, nie klienta
Czasami 403 nie jest oceną ciebie w ogóle — to tryb awaryjny wyzwania, którego nigdy nie próbowałeś. Publiczna dyskusja na GitHub o sprawdzaniu linków na npmjs.com ujmuje to jasno: curl nie może wygenerować poprawnego rozwiązania wyzwania, więc żądanie jest blokowane z 403. Serwer wydaje mały problem JavaScript, czeka chwilę na odpowiedź i odrzuca wszystko, co nie może go wykonać. Żaden zestaw nagłówków, żaden fingerprint i żaden IP nie przejdzie testu, który wymaga uruchomienia kodu.
W tym momencie masz trzy uczciwe opcje: uruchomić prawdziwą przeglądarkę i ponieść koszt, znaleźć punkt końcowy JSON, który sama strona wywołuje (często znajdujący się w tej samej zakładce Network, którą już masz otwartą), lub przekazać URL do usługi, która renderuje na żądanie. QuantumProxies Scraper API robi to ostatnie — TLS na poziomie przeglądarki, wyjścia rezydencjalne, renderowanie JavaScript tylko tam, gdzie strona tego potrzebuje, i markdown, JSON lub surowe HTML z jednego żądania. Jeśli to, co otrzymujesz, to pusta strona zamiast zabronionej, to inna diagnoza: zobacz dlaczego twój scraper zwraca pustą stronę. A jeśli strona blokady zawiera Cloudflare Ray ID, przejdź do Cloudflare error 1020.
Najczęściej zadawane pytania
Dlaczego curl dostaje 403, gdy moja przeglądarka nie?
Ponieważ curl wysyła około trzech nagłówków, bez ciasteczek, bez referera i nieprzeglądarkowy TLS fingerprint, podczas gdy twoja przeglądarka wysyła kilkanaście nagłówków, słoik ciasteczek i handshake Chrome. Serwer odrzuca żądanie, nie zasób. Odtwórz dokładne żądanie przeglądarki za pomocą "Copy as cURL", a następnie usuwaj nagłówki jeden po drugim, aby znaleźć, która różnica ma znaczenie.
Dlaczego wget działa tam, gdzie curl dostaje 403?
Prawie zawsze User-Agent. Niektóre serwery blokują UA curl specjalnie po nadużyciach, pozostawiając wget bez zmian — udokumentowany przypadek pokazał nagłówek odpowiedzi Vary: User-Agent potwierdzający, że serwer rozgałęzia się na jego podstawie, a curl -A 'Wget/1.21.2' przywróciło 200. wget również domyślnie wysyła Accept-Encoding i Connection, co czasami ma znaczenie.
Jak ustawić User-Agent w curl?
Użyj -A 'string', lub równoważnego -H 'User-Agent: string'. Preferuj pełny, aktualny ciąg przeglądarki nad skróconym Mozilla/5.0, który niektóre serwery teraz odrzucają właśnie dlatego, że żadna prawdziwa przeglądarka nie wysyła tylko dwóch tokenów. Połącz go z odpowiadającymi wartościami Accept i Accept-Language, aby cały zestaw pozostał spójny.
Co oznacza błąd curl 22?
Kod wyjścia 22 jest produkowany przez -f/--fail zawsze, gdy serwer zwraca błąd HTTP, a komunikat cytuje status — najczęściej 403. Jest to flaga raportująca, a nie odrębna usterka. Usuń -f, aby zobaczyć ciało odpowiedzi, które zazwyczaj lepiej wyjaśnia blokadę niż sam kod wyjścia.
Czy proxy może naprawić curl 403?
Naprawia podzbiór spowodowany reputacją IP lub geografią — duży podzbiór, gdy twój skrypt działa na hostingu w chmurze, a przeglądarka nie. Nie naprawi brakującego referera, nieobecnego ciasteczka ani wyzwania JavaScript. Najpierw przetestuj nagłówki, ponieważ nic nie kosztują, a następnie zmień wyjściowy IP, aby wyizolować warstwę sieci.
Nie ma tu tajemnicy, tylko luka: przeglądarka wysłała jedno żądanie, a ty wysłałeś inne. Skopiuj to przeglądarki, zmniejsz je, aż się zepsuje, a zawsze znajdziesz element, który miał znaczenie — zazwyczaj nagłówek, czasami fingerprint, okazjonalnie wyzwanie, które wymaga prawdziwej przeglądarki do odpowiedzi.