Tony Wang9 min de lecturaTu scraper funciona en local pero da 403 en el servidor. Aquí está el porqué.
¿Tu scraper funciona en local pero da 403 desde un servidor? Suele ser reputación de IP, huella TLS o detección headless.
Tu scraper corre perfecto en tu laptop. Lo despliegas en un VPS o en un runner de CI, no cambias nada en el código, y de repente todas las solicitudes regresan 403. Se siente como un bug — el código es idéntico — pero normalmente no lo es. Los sistemas anti-bot evalúan una solicitud con base en muchas señales a la vez, y pasar de tu máquina doméstica a un datacenter cambia varias de ellas al mismo tiempo.
Este post desglosa exactamente qué señales cambian, cómo saber cuál te está bloqueando y cómo solucionarlo — para acceso autorizado a datos públicos (mantendremos ese enfoque honesto durante todo el artículo; nada aquí trata de vencer una protección).
Una solicitud se evalúa en dos etapas
Ayuda saber que la detección ocurre en dos etapas:
- Etapa 1 — antes de que se sirva cualquier HTML. La reputación de IP, tu handshake TLS, tu configuración de HTTP/2 y el orden de encabezados se inspeccionan en la conexión misma, de forma pasiva y barata, antes de que tu solicitud siquiera se haya leído por completo.
- Etapa 2 — solo si la Etapa 1 pasa. El JavaScript se ejecuta en la página y revisa las APIs del navegador, canvas/WebGL y el comportamiento.
Una solicitud desde un datacenter normalmente muere en la Etapa 1 — por eso obtienes un 403 silencioso sin página de desafío, no un CAPTCHA. Esa sola observación es tu mejor diagnóstico inicial:
403 sin página de desafío → fallaste en la capa de red (IP o TLS). Una página de desafío o CAPTCHA → pasaste la capa de red y fallaste en la verificación de navegador/comportamiento.
Por qué el mismo código da 403 desde un servidor
1. Reputación de IP — la razón individual más grande
Los sistemas anti-bot clasifican el ASN de tu conexión en los primeros milisegundos. Las principales redes de nube y hosting — AWS, GCP, Azure, Hetzner, DigitalOcean, OVH — y los rangos de CI/serverless detrás de GitHub Actions y Vercel están marcados de antemano con solo verlos. Las IPs residenciales y móviles no lo están.
La reputación de IP es la única señal que usa cada vendedor importante, y varios bloquean ASNs de datacenter antes de siquiera servir el desafío de JavaScript. La verdad cruda: un cliente HTTP simple en una IP residencial limpia normalmente supera a un navegador perfectamente parchado en una IP de datacenter marcada. Tu laptop está en una IP residencial; tu servidor no. Eso solo ya explica la mayoría de los casos de «funciona localmente, 403 en el servidor».
2. Huella TLS (JA3 / JA4)
El handshake TLS en sí identifica al cliente. El protocolo permite muchas combinaciones válidas de cifrados/extensiones, pero cada navegador usa una fija y reconocible — su huella JA3/JA4. Los clientes genéricos tienen handshakes delatores: requests/urllib de Python envían un ClientHello estático de OpenSSL que los vendedores anti-bot han visto millones de veces; net/http de Go, Node y curl por defecto tienen cada uno su propia forma que no es de navegador.
El golpe definitivo es la inconsistencia: si tu encabezado User-Agent dice «Chrome 145» pero tu handshake TLS dice «Python», esa contradicción es prueba definitiva de que la solicitud no viene de un navegador real. En tu laptop a menudo pruebas con un navegador real (o una herramienta cuya TLS coincide); el cliente desnudo de tu servidor no.
3. Huella de HTTP/2 y orden de encabezados
Sobre HTTP/2 el cliente también se delata a través de los valores de su frame SETTINGS, la prioridad de streams y el orden de pseudo-encabezados. El estándar exige :method, :authority, :scheme, :path primero pero no fija su orden, así que cada navegador elige uno (Chrome m,a,s,p; Firefox m,p,a,s; Safari m,s,p,a) — y la mayoría de las bibliotecas HTTP usan un orden que no coincide con ningún navegador. El mayúsculas/minúsculas y orden de encabezados y la ausencia de los client hints Sec-CH-UA / Sec-Fetch-* suman a la señal.
4. Detección headless / de automatización
Por esto Playwright o Puppeteer en un VPS son detectados incluso con una buena IP. Más allá del conocido navigator.webdriver y las señales de canvas (mayormente ya saturadas), la señal profunda es el protocolo de automatización: Playwright, Puppeteer y Selenium controlan Chrome a través del DevTools Protocol y llaman a Runtime.enable al arrancar, algo que unas pocas líneas de JavaScript en la página pueden detectar. Lo crítico es que esto se dispara incluso en una máquina real con una IP residencial perfecta — es un problema de «cómo se controla el navegador», no de IP. (Herramientas que controlan Chrome sobre CDP puro sin esa llamada inicial, como nodriver, lo evitan.)
5. Coherencia de forma — la que casi nadie nota
Un proxy solo reescribe tu IP de origen. Todo lo demás por encima de TCP sigue originándose en el host: el handshake TLS/JA4, el orden de HTTP/2, navigator.platform y el UA, canvas/WebGL/fuentes (la GPU y las fuentes de tu host), el tamaño de pantalla. Así que un VPS con Linux detrás de un proxy residencial anuncia un navegador con forma de Linux desde una IP «residencial» — una contradicción que un Mac o una máquina doméstica con Windows real jamás produce. En la práctica, un servidor Linux con proxy puede terminar bloqueado más que tu laptop sin proxy. Esta es la explicación más limpia de «funciona en mi máquina, muere en el servidor».
¿Con qué sistema estoy lidiando?
La página de bloqueo y las cookies normalmente te dicen contra qué vendedor estás:
| Sistema anti-bot | Cómo identificarlo | Se basa principalmente en |
|---|---|---|
| Cloudflare | Encabezado CF-RAY; cookies cf_clearance / __cf_bm | TLS/JA3 + HTTP/2 que coincide con tu UA; bloquea ASNs de datacenter antes del desafío |
| Akamai Bot Manager | Cookie _abck; script akamai-bm-telemetry | telemetría de comportamiento validada en servidor — una cookie copiada no funciona |
| DataDome | Encabezados X-DataDome-*; cookie datadome | puntuación ML en tiempo real por solicitud; CAPTCHA de deslizador |
| Imperva / Incapsula | Desafío reese84; cookies incap_ses_* | prueba de trabajo + huella que hay que resolver, no copiar |
| PerimeterX / HUMAN | Cookies _px3 / _pxhd; px.js | puntuación de IP en backend primero, después biometría de comportamiento |
Para saber qué tan común es cada uno en la web, revisa nuestro Índice de Adopción Anti-Bot — poco más de la mitad de la web accesible corre un anti-bot gestionado, mayormente Cloudflare.
Cómo saber qué capa te está bloqueando
Una escalera de aislamiento limpia — cada paso cambia exactamente una variable:
- Identifica al vendedor a partir de los encabezados/cookies de respuesta (tabla arriba). Eso te dice qué capa sospechar.
- ¿Es la IP? Corre el mismo script desde tu laptop (residencial) y desde el servidor. Funciona en residencial, 403 en el servidor → reputación de IP. Confírmalo enrutando la solicitud del servidor a través de un proxy residencial; si ahora pasa, la IP era la barrera.
- ¿Es TLS? En la misma IP, cambia un cliente simple por uno que imite la TLS de un navegador:
# cliente simple — a menudo 403 en la capa de red
curl -s -o /dev/null -w "%{http_code}\n" https://example.com
# huella TLS igualada — ¿pasa donde el cliente simple falló?
python -c "from curl_cffi import requests; \
print(requests.get('https://example.com', impersonate='chrome').status_code)"
Si el cliente que imita pasa donde requests/curl recibió un 403, tu huella TLS/JA3 era la barrera.
- ¿Necesita JS / es headless? Si la suplantación de TLS sigue fallando, abre la página a mano en un navegador real, con interfaz, desde la misma red. El navegador manual pasa pero tu automatización falla → detección headless / de automatización, no IP ni TLS.
- Verificación de coherencia de forma. ¿Sigues bloqueado en un VPS con Linux detrás de un proxy residencial? Corre el mismo trabajo desde una máquina residencial cuyo sistema operativo coincida con el UA que declaras. Si eso pasa, la incoherencia de forma del VPS era el problema — y la solución es «correr el navegador en el host residencial», no «agregar otro proxy».
Cómo solucionarlo (en orden, con honestidad)
- Sal de las IPs de datacenter. Los proxies residenciales / ISP / móviles arreglan la capa que revisa todo vendedor y la que la mayoría de los servidores fallan primero. Compensación: costo (el ancho de banda se mide por GB, así que las páginas pesadas en JS se acumulan) — usa proveedores de reputación y rota para trabajos sostenidos.
- Iguala la huella TLS de un navegador real. Bibliotecas como
curl_cffi(Python),rnet/wreq(Rust) otls-clientenvían un ClientHello y una configuración de HTTP/2 idénticos a los de un navegador sin necesitar un proceso de navegador — rápido y barato. Límite: no hay motor de JavaScript, así que son inútiles en páginas renderizadas con JS, y la suplantación se detecta cada vez más. Funciona muy bien en la mayoría de páginas protegidas solo en la capa de red; no es una bala de plata. - Sigilo con navegador real + escalamiento inteligente. Para páginas que de verdad necesitan JavaScript, controla un Chrome real con la huella de automatización eliminada (por ejemplo,
nodriver), y escala: prueba primero un cliente HTTP con huella falsificada barata, y solo levanta un navegador sigiloso para la fracción que lo necesita. Corre ese navegador en el host que posee la IP residencial (coherencia de forma otra vez). - Desafíos. Cuando una página presenta un desafío interactivo, el camino honesto es resolverlo en un navegador real que ejecute el desafío legítimamente — el
_abckde Akamai y elreese84de Imperva se validan en servidor, así que un token copiado no funciona; la respuesta tiene que producirse genuinamente. Enmárcalo como acceso autorizado a datos públicos; nunca como «vencer el CAPTCHA». - Delega a una API gestionada. Una API de scraping/desbloqueo mantenida se encarga de todo lo anterior detrás de una sola llamada — rotación residencial, TLS que imita navegadores, huellas de navegador real, manejo de sesión y de desafíos. Es la decisión correcta cuando estás lidiando con varios vendedores, operando a escala, o trabajando en un equipo pequeño, porque el costo oculto de hacerlo tú mismo es el mantenimiento continuo: la actualización de desafío de cada vendedor puede romper tu bypass de la noche a la mañana. (Esa es la apuesta sobre la que está construido Crawlora: pagas solo por éxito, con la ruta de acceso mantenida por nosotros.)
El panorama más amplio: la web se está cerrando
Esta brecha entre «funciona localmente» y «funciona a escala» se está ampliando, no cerrando. En 2026 Cloudflare bloquea a los rastreadores de IA por defecto y está desplegando pay-per-crawl, y aproximadamente la mitad de la web accesible ahora corre un anti-bot gestionado. Las IPs de datacenter son cada vez menos útiles, el aprendizaje automático anti-bot sigue adaptándose, y los bypasses hechos en casa se degradan más rápido. Las opciones honestas son las mismas dos de siempre — invertir en infraestructura seria (IPs residenciales + sigilo con Chrome real + escalamiento inteligente), o delegarlo a un servicio gestionado — y, en cualquier caso, recolectar solo datos públicos a los que estés autorizado a acceder.
Fuentes
Dónde encaja esto
Verifica antes de construir: pasa el objetivo por el Verificador Anti-Bot gratuito para ver si está protegido y cómo, y navega el Índice de Adopción Anti-Bot para ver qué tan común es cada vendedor. Para el cambio más amplio del que esto forma parte, mira por qué Reddit bloqueó el JSON no autenticado y si el web scraping es legal en 2026.
Cuando prefieras no mantener tú mismo proxies, suplantación de TLS y sigilo headless, Crawlora devuelve JSON estructurado limpio desde una sola clave y mantiene la ruta de acceso funcionando conforme los sitios se endurecen — prueba un endpoint en el Playground y revisa los costos en créditos en la página de precios.
Preguntas frecuentes
Why does my scraper work locally but get a 403 on a server?
Moving from your laptop to a datacenter flips several signals at once. Anti-bot systems judge a request on many layers — IP reputation, TLS fingerprint, HTTP/2 shape, headers, and how the browser is driven — and failing any one returns a 403. Your home machine passes because every layer is consistent with a real residential browser; a server changes at least the IP (a flagged datacenter range), and often the TLS/JS fingerprint too, and that inconsistency is what gets caught.
Is the block my IP, my TLS fingerprint, or headless detection?
Isolate it one variable at a time. Run the same script from a residential IP vs the server: if it works on residential and 403s on the server, it's IP reputation. On the same IP, swap a plain client for one that mimics a browser's TLS (e.g. curl_cffi with impersonate='chrome'): if that passes, it was your TLS/JA3 fingerprint. If impersonation still fails, open the page in a real headful browser by hand — if that works but your automation doesn't, it's headless/automation detection. Shortcut: a 403 with no challenge page means the network layer (IP/TLS); a CAPTCHA page means you passed it and failed the browser check.
Do residential proxies fix a 403 on a server?
Often yes — moving off datacenter IPs onto residential or ISP IPs fixes the one signal every anti-bot vendor checks, and the one most servers fail first. But it's necessary, not always sufficient: if your TLS or headless fingerprint still looks like a library or an automated browser, a clean IP alone won't pass. And a proxy only changes the IP — a Linux VPS behind a residential proxy still leaks a Linux-shaped TLS/JS fingerprint, so for JS-heavy targets you may also need to run a real browser on a residential host.
Why doesn't adding a User-Agent header fix the 403?
The User-Agent is one of the weakest signals, and faking it can make things worse. If your header says 'Chrome' but your TLS handshake, HTTP/2 settings and header order say 'Python', that mismatch is definitive proof you're not a real browser. The block is at the IP-reputation and fingerprint layer, not the User-Agent string — so 'add a User-Agent' or 'slow down the rate' doesn't address what's actually being detected.
Why does Playwright get blocked on a VPS even with a good IP?
Beyond the obvious tells (navigator.webdriver, canvas quirks), the deep one is the automation protocol. Playwright, Puppeteer and Selenium drive Chrome over the DevTools Protocol and call Runtime.enable at startup, which a few lines of page JavaScript can detect — and it fires even on a real machine with a perfect residential IP, because it's a 'how the browser is driven' problem, not an IP one. Tools that drive Chrome over plain CDP without that call (like nodriver) avoid it.
Is it legal to scrape public data this way?
Collecting publicly accessible data is broadly defensible, but the law turns on what you collect and how you use it — respect each site's terms and robots directives, avoid personal data and copyrighted content at scale, and never use these techniques to get past a login or paywall. Frame everything as authorized access to public data. This is not legal advice.