# Por qué tu agente de programación recibe «CONNECT tunnel failed, response 403»

Source: https://ipvolt.com/es/blog/connect-tunnel-failed-403
Markdown: https://ipvolt.com/es/blog/connect-tunnel-failed-403.md
Language: es

[Inicio de ipvolt](https://ipvolt.com/es.md) / [Blog](https://ipvolt.com/es/blog.md) / CONNECT tunnel failed, response 403: por qué afecta a agentes de IA

Análisis
Publicado: 2026-10-02
Actualizado: 2026-10-02
Por ipvolt
8 min de lectura

El curl de tu agente falla con CONNECT tunnel failed, response 403 mientras otros hosts funcionan. El proxy rechazó el túnel. Cómo saber quién lo bloqueó y qué arreglar.

```text
curl: (56) CONNECT tunnel failed, response 403
```

Eso es curl 8.15 a 8.17 pidiendo a un proxy que abra un túnel hacia un host `https://` y recibiendo una negativa. curl 8.18.0 y 8.22.0 imprimen el mismo mensaje con código de salida 7 en lugar de 56. El mismo rechazo, registrado a través de un único proxy de prueba local, se ve así en Python Requests 2.34.2, que lanza `requests.exceptions.ProxyError` con este mensaje:

```text
HTTPSConnectionPool(host='example.org', port=443): Max retries exceeded with url: / (Caused by ProxyError('Unable to connect to proxy', OSError('Tunnel connection failed: 403 Forbidden')))
```

Y en el `fetch` integrado de Node.js 24.20.0 con `NODE_USE_ENV_PROXY=1`, imprimiendo un nivel de `err.cause` por línea:

```text
depth 0: TypeError: fetch failed
depth 1: DOMException: Request was cancelled.
depth 2: RequestAbortedError [UND_ERR_ABORTED]: Proxy response (403) !== 200 when HTTP Tunneling
```

En los tres casos, el proxy dijo que no antes de que tu petición saliera de él. A través del mismo proxy, `https://example.com/` devolvió 200 en todos los clientes. Solo cambió el host.

## Qué significa el 403

Para una URL `https://`, el cliente envía `CONNECT example.org:443` al proxy y espera permiso para abrir un túnel. El RFC 9110 dice que cualquier respuesta 2xx pasa la conexión a modo túnel y que «cualquier respuesta que no sea satisfactoria indica que el túnel todavía no se ha formado». Un 403 es el proxy negándose a abrir ese túnel. [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect)

De ahí salen dos cosas:

- **No es un problema de credenciales.** Un proxy que quiere credenciales responde 407 con un desafío `Proxy-Authenticate`; la [guía del 407](/es/guides/fix-proxy-error-407) cubre ese caso. Un 403 significa que el proxy decidió no reenviar esta petición, así que cambiar la contraseña no servirá.
- **El sitio de destino nunca se alcanzó.** Sin túnel no hay handshake TLS ni petición a `example.org`. El write-out de curl lo muestra: `connect=403` es la respuesta del proxy y `http=000` significa que el sitio nunca respondió.

## Por qué les pasa a los agentes de programación

Los sandboxes y los runners de CI suelen enviar todo el tráfico de salida a través de un proxy de egress con una lista de permitidos. La dirección del proxy llega en `HTTPS_PROXY`, así que curl, pip, npm y `fetch` pasan todos por él. Los hosts de la lista obtienen un túnel; todo lo demás recibe un 403. Por eso un host funciona y el siguiente falla desde la misma shell.

Dos informes públicos muestran este patrón:

- En [anthropics/claude-code#56959](https://github.com/anthropics/claude-code/issues/56959), Claude Code ejecutándose con Bedrock tenía `HTTPS_PROXY` apuntando al propio proxy del sandbox en localhost. Ese proxy permitía cuatro hosts (`bedrock-runtime.us-east-1.amazonaws.com`, `api.anthropic.com` y dos patrones de Sentry) y, en palabras de quien lo reportó, «cualquier otro host devuelve HTTP 403 desde el proxy local». `curl https://github.com` fallaba con `curl: (56) CONNECT tunnel failed, response 403`. Quien lo reportó dijo que las entradas de `sandbox.network.allowedDomains` en la configuración de usuario y en la gestionada se ignoraban en ese modo. La incidencia se cerró como duplicada de #37970.
- En [SocketDev/socket-sdk-js#659](https://github.com/SocketDev/socket-sdk-js/issues/659), un agente que ejecutaba una actualización semanal de dependencias en CI no pudo terminar. La herramienta de actualización, taze, buscaba versiones en `npm.antfu.dev`, que «bloquea el firewall del sandbox de CI (403 CONNECT tunnel failed)». curl a ese host devolvió HTTP 000 con el error 56 de curl, mientras que `curl https://registry.npmjs.org/semver` devolvió 200. Los mantenedores lo arreglaron manteniendo las búsquedas en `registry.npmjs.org`, que ya estaba en la lista de permitidos.

## Cómo saber quién dijo que no

Ejecuta una vez la URL que falla con `curl -v` a través del mismo proxy. Las líneas que empiezan por `>` son lo que envió curl; las que empiezan por `<` antes de `CONNECT tunnel failed` son la respuesta del proxy. Esto es curl 8.16.0 contra el proxy de prueba:

```text
*   Trying 127.0.0.1:41745...
* CONNECT: no ALPN negotiated
* allocate connect buffer
* Establish HTTP proxy tunnel to example.org:443
> CONNECT example.org:443 HTTP/1.1
> Host: example.org:443
> User-Agent: curl/8.16.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain
< Content-Length: 32
< Connection: close
< 
* CONNECT tunnel failed, response 403
* closing connection #0
curl: (56) CONNECT tunnel failed, response 403
```

La línea `Trying` nombra al proxy, no al sitio. El 403 llega como respuesta al `CONNECT` y no hay líneas de TLS después, así que el sitio nunca participó. Si en cambio un 403 aparece después de un `CONNECT` correcto y un handshake TLS, viene del sitio web y el proxy no es el problema.

Después compara un host permitido y uno bloqueado a través del mismo proxy, usando el write-out para separar los dos códigos de estado:

```sh
curl --disable -sS -o /dev/null -x http://127.0.0.1:41745 -w 'http=%{http_code} connect=%{http_connect} exit=%{exitcode}\n' https://example.com/
curl --disable -sS -o /dev/null -x http://127.0.0.1:41745 -w 'http=%{http_code} connect=%{http_connect} exit=%{exitcode}\n' https://example.org/
```

Con curl 8.18.0 el primero imprimió `http=200 connect=200 exit=0`. El segundo imprimió `http=000 connect=403 exit=7` y `curl: (7) CONNECT tunnel failed, response 403`. Mismo cliente, mismo proxy, distinto host: el proxy tiene una regla por host. Repite la comparación con tus propios hosts permitido y bloqueado. En el sandbox de un agente, usa un host que sepas que funciona, como el registro de paquetes al que el agente ya llegó.

Por último, comprueba qué proxy usa realmente el proceso. Imprime `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY` y sus variantes en minúsculas en la misma shell o el mismo paso del job que falla. Un host incluido en `NO_PROXY` se salta el proxy y conecta directamente, algo que un sandbox puede bloquear de otra manera. La [guía de variables de entorno de proxy](/es/guides/proxy-environment-variables) explica cómo eligen los clientes entre estas variables, y [NO_PROXY matching, tested](/es/blog/no-proxy-matching-tested) muestra que los clientes no coinciden en qué encaja con una entrada de `NO_PROXY`. El `fetch` integrado de Node ignora estas variables salvo que se defina `NODE_USE_ENV_PROXY=1` o `--use-env-proxy`, así que un script de Node y un comando curl en la misma shell pueden tomar rutas distintas. [Documentación de la CLI de Node.js](https://nodejs.org/api/cli.html#node_use_env_proxy1)

## Arréglalo sin rodear el proxy

El 403 es una decisión de política, así que el arreglo está en el lado de la política o en el de la fuente:

- **Permite el host.** Añade el dominio exacto que necesita la herramienta a la lista de permitidos de egress del sandbox o del CI. Toma el dominio de la línea `CONNECT` de `curl -v` o del propio error de la herramienta, no de su configuración: en el informe de Socket, taze buscaba versiones en `npm.antfu.dev`, no en el registro de npm.
- **Usa una fuente que ya esté permitida.** Un espejo, un registro interno o el registro oficial que la política ya permite suele evitar el cambio de política. Ese fue el arreglo de Socket.
- **Revisa el puerto.** Algunos proxies solo abren túneles al puerto 443. La configuración sugerida de Squid incluye `http_access deny CONNECT !SSL_ports`, con `SSL_ports` definido como el puerto 443. [Squid http_access](http://www.squid-cache.org/Doc/config/http_access/) En el proxy de prueba, que permitía `example.com` solo en el puerto 443, `https://example.com:8443/` recibió el mismo `CONNECT tunnel failed, response 403` que el host bloqueado.
- **Revisa las reglas de destino del proveedor.** Los proveedores comerciales de proxies también pueden rechazar destinos. El catálogo de errores de Bright Data enumera errores de política bajo HTTP 403, por ejemplo «You tried to target %HOST% which is blocked by Bright Data policy». [Catálogo de errores de Bright Data](https://docs.brightdata.com/proxy-networks/errorCatalog) Decodo enumera categorías de destinos, como sitios bancarios y gubernamentales, que restringe por defecto. [Destinos restringidos de Decodo](https://help.decodo.com/docs/residential-proxy-restricted-targets)

No rodees las reglas de egress de un sandbox. Quitar `HTTPS_PROXY`, añadir el host a `NO_PROXY` o enrutar por otro proxy para saltarte una lista de permitidos anula el control que alguien puso a propósito, y puede que simplemente no funcione: quien reportó #56959 comprobó que el sandbox también bloqueaba el acceso directo a la red por debajo del proxy. Pide a quien gestiona el sandbox o el runner de CI que permita el host y dale la línea `CONNECT` exacta de `curl -v`. El agente del informe de Socket hizo justo eso: se detuvo, informó del host bloqueado y no intentó evadir el firewall.

## Por qué curl informa distinto con http://

Para una URL `http://` no hay `CONNECT`. curl envía la petición completa al proxy, y el 403 del proxy es la respuesta a esa petición. El proxy de prueba respondió 403 en ambos casos, pero curl los informó de forma distinta:

| A través del mismo proxy | curl 8.15.0 a 8.17.0 | curl 8.18.0 y 8.22.0 |
| --- | --- | --- |
| `https://example.org/` (bloqueado) | salida 56, `curl: (56) CONNECT tunnel failed, response 403` | salida 7, `curl: (7) CONNECT tunnel failed, response 403` |
| `http://example.org/` (bloqueado) | salida 0, la página 403 del proxy impresa como cuerpo | salida 0, la página 403 del proxy impresa como cuerpo |
| `http://example.org/` con `--fail` | salida 22, `curl: (22) The requested URL returned error: 403` | salida 22, `curl: (22) The requested URL returned error: 403` |
| `https://example.com/` (permitido) | salida 0, `http=200 connect=200` | salida 0, `http=200 connect=200` |

En el caso `http://`, `curl -v` muestra `> GET http://example.org/ HTTP/1.1` seguido de `< HTTP/1.1 403 Forbidden`, y curl imprime el cuerpo. El mantenedor de curl Daniel Stenberg describió la diferencia en la [discusión de curl #15718](https://github.com/curl/curl/discussions/15718), que trata de un 407 pero se aplica a cualquier rechazo: no lograr establecer el túnel significa que la transferencia no se realiza, lo cual es un fallo, mientras que un GET a un proxy que devuelve una respuesta «es una transferencia *correcta* que contiene un código 4xx». Por eso una URL `http://` en un script sin `--fail` puede parecer un éxito.

Los demás clientes se dividen igual, con una excepción. Python Requests devolvió un `Response` normal con estado 403 para `http://example.org/`. El `fetch` de Node.js 24.20.0 con `NODE_USE_ENV_PROXY=1` abrió un túnel `CONNECT example.org:80` incluso para la URL `http://`, así que falló con la misma causa `Proxy response (403) !== 200 when HTTP Tunneling`.

Un detalle más de versiones: la compilación estática de curl 8.22.0 que usamos incluye HTTP/3. Cuando el host anunciaba HTTP/3, primero intentó un túnel `CONNECT-UDP`, así que `-v` mostró dos respuestas 403 antes del `curl: (7) CONNECT tunnel failed, response 403` final.

## Reprodúcelo

Todo lo anterior se registró el 2 de octubre de 2026 con el laboratorio de la [carpeta de descarga](/downloads/connect-tunnel-failed-403/README.md): un proxy de Node.js en `127.0.0.1` que solo abre túneles a `example.com` en el puerto 443 y responde 403 a cualquier otro `CONNECT` y a las peticiones HTTP normales, con curl 8.15.0, 8.16.0, 8.17.0, 8.18.0 y 8.22.0, Python 3.14.4 con Requests 2.34.2 y urllib3 2.8.0, y Node.js 24.20.0. La salida en bruto está en [results.json](/downloads/connect-tunnel-failed-403/results.json). El cuerpo 403 del proxy de prueba (`403 Forbidden: lab proxy policy`) es suyo; el proxy real de un sandbox o de un proveedor enviará otro texto.

## Relacionado

- [Corrige el error de proxy 407](/es/guides/fix-proxy-error-407), cuando el proxy pide credenciales.
- [Corrige ERR_TUNNEL_CONNECTION_FAILED](/es/guides/fix-err-tunnel-connection-failed), para el mismo rechazo dentro de Chromium, Playwright y Puppeteer.
- [Variables de entorno de proxy](/es/guides/proxy-environment-variables), para saber qué variable lee realmente un cliente.
- [NO_PROXY matching, tested](/es/blog/no-proxy-matching-tested), para entender por qué una entrada de exclusión coincide en un cliente y no en otro.
- [Proxies y cómputo para agentes de IA](/es/blog/ai-agent-proxies-and-compute), para decidir qué partes de un agente necesitan siquiera un proxy.

ipvolt está construyendo infraestructura de proxies para desarrolladores y agentes. [Únete a la lista de espera](https://ipvolt.com/#waitlist-hero) para recibir un correo cuando se abra el acceso.

## Fuentes

- [RFC 9110: CONNECT](https://www.rfc-editor.org/rfc/rfc9110.html#name-connect)
- [anthropics/claude-code#56959: sandbox allowlist answering 403 to CONNECT](https://github.com/anthropics/claude-code/issues/56959)
- [SocketDev/socket-sdk-js#659: CI sandbox firewall blocking one host](https://github.com/SocketDev/socket-sdk-js/issues/659)
- [curl discussion #15718: proxy responses for http and https URLs](https://github.com/curl/curl/discussions/15718)
- [Squid configuration: http_access suggested rules](http://www.squid-cache.org/Doc/config/http_access/)
- [Bright Data: proxy error catalog](https://docs.brightdata.com/proxy-networks/errorCatalog)
- [Decodo: residential proxy restricted targets](https://help.decodo.com/docs/residential-proxy-restricted-targets)
- [Node.js CLI: NODE_USE_ENV_PROXY](https://nodejs.org/api/cli.html#node_use_env_proxy1)

## Entérate cuando se abra el acceso a ipvolt.

Ya que estás aquí

ipvolt está en desarrollo. Deja tu correo y te avisaremos una sola vez cuando se abra el acceso.

Un correo cuando se abra el acceso. Nada más.

[Solicitar acceso anticipado](https://ipvolt.com/es/blog/connect-tunnel-failed-403#waitlist-blog-end)

[Privacidad](https://ipvolt.com/privacy)


## Artículos relacionados

- [Parsear precios japoneses: yenes, ancho completo y etiquetas fiscales](https://ipvolt.com/es/blog/japanese-price-parsing.md) (Análisis, 6 oct 2026, 7 min de lectura): Parsea precios japoneses sin perder la divisa ni la base fiscal, con un fixture de Python probado: yen de ancho completo, precios mixtos y casos de revisión deliberados.
- [SOCKS5 vs SOCKS5h: diferencias y qué envían 6 clientes (probado)](https://ipvolt.com/es/blog/socks5-vs-socks5h.md) (Comparativa, 4 oct 2026, 8 min de lectura): socks5:// no significa DNS local en todos los clientes. Registramos qué envían curl, Requests, HTTPX, aiohttp, Playwright y Node a un proxy SOCKS5 con cada esquema.
- [API de cuotas de apuestas o scraping: hoja de costes](https://ipvolt.com/es/blog/betting-odds-api-vs-scraping.md) (Comparativa, 28 sept 2026, 9 min de lectura): Compara las API de cuotas y el scraping autorizado por cobertura, frescura y coste de recogida modelado con una hoja editable y supuestos de carga explícitos.

## Guías relacionadas

- [Corrige el error de proxy 407 sin adivinar](https://ipvolt.com/es/guides/fix-proxy-error-407.md): Diagnostica Proxy Authentication Required en un orden repetible: gateway, método de autenticación, codificación de credenciales, alcance de cuenta y ajustes del cliente.
- [Corrige ERR_TUNNEL_CONNECTION_FAILED en Playwright y Puppeteer](https://ipvolt.com/es/guides/fix-err-tunnel-connection-failed.md): ERR_TUNNEL_CONNECTION_FAILED y ERR_PROXY_CONNECTION_FAILED explicados con una matriz verificada de fallos de proxy y cómo recuperar el estado CONNECT que Chromium oculta.
- [Variables de entorno de proxy: HTTP_PROXY y NO_PROXY](https://ipvolt.com/es/guides/proxy-environment-variables.md): Diagnostica las diferencias de enrutamiento de HTTP_PROXY, HTTPS_PROXY, ALL_PROXY y NO_PROXY en curl, Python Requests y Node.js con una comprobación local aislada.

## Sobre ipvolt

Análisis técnico del equipo de ipvolt.

El acceso a ipvolt aún no está abierto.

[Leer el original en inglés](https://ipvolt.com/blog/connect-tunnel-failed-403.md)
