Integración7 min de lectura

Usar un proxy con curl: -x, variables de entorno, SOCKS5 y auth

Cómo usar un proxy con curl: la opción -x, las variables http_proxy y https_proxy, SOCKS5 con socks5h, autenticación del proxy y cómo leer los errores CONNECT y 407.

En esta página

El punto de partida

Todas las formas en que curl elige un proxy, desde una sola opción -x hasta las variables de entorno, con los detalles de autenticación, SOCKS5 y túnel que deciden si la petición tiene éxito.

Configurar un proxy para una sola petición con -x

La opción -x, cuya forma larga es --proxy, enruta una sola petición de curl a través de un proxy. Indícale el esquema, el host y el puerto del proxy. Cuando se omite el esquema, curl asume http://, y cuando se omite el puerto asume 1080, así que escribe ambos de forma explícita.

El esquema describe la conexión con el proxy, no con el destino. Un destino https:// a través de un proxy HTTP ordinario sigue usando -x http://host:port; curl abre un túnel CONNECT a través de él. Escribe -x https://host:port solo cuando el proveedor termina el TLS en el propio proxy. curl también acepta aquí socks4://, socks4a://, socks5:// y socks5h://.

El comando de abajo conserva dos hábitos del resto de esta guía: --disable ignora un curlrc que podría añadir su propio proxy, y --noproxy '' cancela cualquier exclusión no_proxy heredada, de modo que la petición pase de verdad por el gateway que indicaste. proxy.example.invalid no se puede resolver a propósito; sustitúyelo por el gateway de tu proveedor. Sin --compressed, el comando no envía la cabecera Accept-Encoding, así que un servidor que solo comprime cuando se lo piden devuelve la página completa sin comprimir a través del proxy; si pagas por GB, consulta qué clientes no piden compresión y cómo comprobar los bytes.

curl -x · una petición a través de un proxy

sh
curl --disable --noproxy '' \
  -x http://proxy.example.invalid:8080 \
  https://example.com/

Autenticación del proxy: -U, --proxy-user y 407

Las credenciales del proxy son independientes de las credenciales del destino. -U o --proxy-user las envía al proxy; -u o --user las envía al destino. Mezclar las dos produce un 407 del proxy o un 401 del destino aunque la contraseña sea correcta.

También puedes incrustar las credenciales en la URL del proxy como http://user:password@host:port. Codifica primero con porcentaje los caracteres reservados: una @ en la contraseña debe convertirse en %40 o curl la interpretará como el inicio del host. Basic es el esquema de autenticación de proxy por defecto; --proxy-digest, --proxy-ntlm y --proxy-anyauth seleccionan otros cuando el proveedor los documenta.

Escribir la contraseña directamente puede guardarla en el historial del shell y la expone en los argumentos iniciales del proceso. Una variable puede evitar escribirla literalmente si la proporcionas de forma privada, pero --proxy-user "$PROXY_USERNAME:$PROXY_PASSWORD" sigue expandiéndola en un argumento antes de iniciar curl. En los sistemas compatibles, curl oculta ese argumento después del inicio; una lista de procesos tomada más tarde puede no mostrar la exposición. Consulta la documentación de proxy-user de curl.

Para scripts con curl 8.3.0 o posterior, el ejemplo siguiente importa las variables exportadas dentro de curl. Haz que tu gestor de secretos las proporcione, desactiva el trazado del shell y no pegues comandos export que contengan secretos en el historial. Esto evita una contraseña en los argumentos de curl; no oculta el entorno heredado ni la memoria a cuentas con permiso para inspeccionarlos. Para una comprobación interactiva, --proxy-user username sin dos puntos solicita la contraseña.

El manejo de argumentos no cifra la conexión con el proxy. Las credenciales HTTP Basic enviadas a un proxy http:// no quedan protegidas por el posterior túnel HTTPS al destino. Usa un gateway https:// si tu proveedor lo admite y mantén la verificación del certificado del proxy.

  • connect=407 o el mensaje Received HTTP code 407 from proxy after CONNECT significa que el proxy rechazó las credenciales o no recibió ninguna. Revisa el valor de -U, la codificación y el formato de nombre de usuario del proveedor.
  • Un 401 del destino cuando el túnel tuvo éxito es un problema del destino; el paso del proxy ya está funcionando.
  • Si tu proveedor codifica opciones de país, sesión o protocolo en el nombre de usuario, sigue su formato documentado. --proxy-user espera el usuario y la contraseña separados por dos puntos; no es un paso de codificación de URL.

curl 8.3+ · importar credenciales dentro de curl

sh
: "${PROXY_USERNAME:?}" "${PROXY_PASSWORD:?}"
curl --disable --noproxy '' \
  -x http://proxy.example.invalid:8080 \
  --variable %PROXY_USERNAME \
  --variable %PROXY_PASSWORD \
  --expand-proxy-user '{{PROXY_USERNAME}}:{{PROXY_PASSWORD}}' \
  https://example.com/

Variables de entorno: http_proxy, https_proxy, no_proxy y .curlrc

Sin -x, curl busca un proxy en el entorno. Lee http_proxy solo en minúsculas, porque el nombre en mayúsculas puede ser establecido por cabeceras de petición CGI; acepta https_proxy o HTTPS_PROXY, all_proxy o ALL_PROXY, y no_proxy o NO_PROXY. La variable se elige según el esquema del destino: un destino https:// usa https_proxy, por lo que http_proxy por sí solo no configura la ruta HTTPS; ALL_PROXY u otros ajustes aún pueden afectarla. curl_cffi, el binding de libcurl para Python, sigue la misma regla de minúsculas; Proxies en curl_cffi la recoge junto con las excepciones que lanzan un CONNECT rechazado y un handshake SOCKS fallido.

no_proxy es una lista separada por comas de nombres de host o sufijos de dominio que evitan el proxy, y un solo * desactiva el proxy por completo. -x sustituye el gateway elegido por las variables de proxy, pero no_proxy aún puede evitarlo. --noproxy sustituye no_proxy; un valor vacío de --noproxy cancela las exclusiones heredadas.

Un archivo ~/.curlrc puede establecer proxy = "http://host:port" para cada invocación, lo cual es cómodo en una estación de trabajo y sorprendente dentro de un contenedor o un trabajo de CI. Ejecuta curl con --disable, o su forma corta -q como primer argumento, cuando un script deba ignorarlo.

http_proxy y https_proxy · sesión de shell

sh
export http_proxy='http://proxy.example.invalid:8080'
export https_proxy='http://proxy.example.invalid:8080'
export no_proxy='localhost,127.0.0.1,.internal.example'
curl https://example.com/         # uses https_proxy
curl --noproxy '*' https://example.com/   # bypasses the proxy once

SOCKS5 con curl: socks5 frente a socks5h

curl habla SOCKS a través de la misma opción -x. socks5:// resuelve el nombre de host del destino en tu máquina y envía al proxy una dirección IP. socks5h:// envía el nombre de host y deja que el proxy lo resuelva, que es lo que quieres cuando el destino debe resolverse desde la red del proxy o cuando el DNS local no debe ver el destino en absoluto. Las opciones largas --socks5 y --socks5-hostname son equivalentes.

La autenticación con nombre de usuario y contraseña para SOCKS5 usa la misma opción --proxy-user. SOCKS no tiene estado HTTP CONNECT; un handshake SOCKS rechazado puede producir el código de salida 97 con un motivo, en lugar de un HTTP 407. Las credenciales expandidas por el shell en este ejemplo de compatibilidad siguen entrando en los argumentos iniciales; usa la importación de curl 8.3+ de arriba cuando debas evitar esa exposición.

curl socks5h · DNS del lado del proxy

sh
curl --disable --noproxy '' \
  -x socks5h://proxy.example.invalid:1080 \
  --proxy-user "$PROXY_USERNAME:$PROXY_PASSWORD" \
  https://example.com/

Destinos HTTPS: el túnel CONNECT y -v

Para un destino https:// a través de un proxy HTTP, curl envía primero CONNECT example.com:443 al proxy. Tras una respuesta CONNECT satisfactoria, curl inicia TLS con el destino dentro del túnel. Un proxy de reenvío ordinario no puede leer ese intercambio HTTP cifrado; un proxy que intercepta TLS y en el que confía tu cliente es un caso distinto. Ejecuta -v para observar ambos pasos; la respuesta CONNECT llega antes que las cabeceras del destino. -p o --proxytunnel fuerza ese túnel también para un destino http://.

Para un destino http:// plano no hay túnel. curl envía la URL completa al proxy, el proxy la obtiene, y las cabeceras de respuesta que ves pueden proceder de cualquiera de las dos partes. Cuando haya que atribuir un código de estado a una capa, prefiere el caso con túnel y lee el estado del CONNECT por separado con la variable de salida %{http_connect} de --write-out.

Con un proxy https://, curl verifica el certificado del proxy además del certificado del destino. --proxy-cacert proporciona un paquete de confianza privado para el proxy; mantén ambas verificaciones activadas en lugar de recurrir a --proxy-insecure o -k.

Envía una petición acotada como línea base

Guarda esto como proxy-check.sh y ejecútalo con sh proxy-check.sh. Requiere curl 8.3.0 o posterior y autenticación de proxy HTTP Basic. Haz que tu gestor de secretos exporte PROXY_USERNAME y PROXY_PASSWORD, y exporta PROXY_URL con el gateway de tu proveedor, con su esquema y puerto; https://proxy.example.invalid:8443 es un valor ilustrativo que a propósito no puede conectar.

curl importa las credenciales por sí mismo, por lo que la contraseña no se pasa en sus argumentos. Las comprobaciones rechazan valores ausentes o vacíos; siguen siendo aplicables los límites del entorno y del trazado. El comando imprime el estado del destino, el estado del CONNECT y el tiempo transcurrido, y descarta el cuerpo de la respuesta. Conserva esta línea base antes de añadir un navegador, concurrencia o código de aplicación.

proxy-check.sh · curl 8.3+

sh
: "${PROXY_URL:?Set your provider proxy URL}"
: "${PROXY_USERNAME:?}" "${PROXY_PASSWORD:?}"
curl --disable --silent --show-error --fail \
  --noproxy '' \
  --proxy "$PROXY_URL" \
  --variable %PROXY_USERNAME \
  --variable %PROXY_PASSWORD \
  --expand-proxy-user '{{PROXY_USERNAME}}:{{PROXY_PASSWORD}}' \
  --connect-timeout 10 --max-time 30 \
  --output /dev/null \
  --write-out 'http=%{http_code} connect=%{http_connect} seconds=%{time_total}\n' \
  'https://example.com/'

Lee los códigos de salida de curl y encuentra la capa que falla

Una prueba sintética local con curl 8.7.1 comparó ambas formas: la expansión del shell incluyó la contraseña en los argumentos iniciales; la importación interna de curl no. Ambas enviaron las mismas credenciales de prueba. Una lista de procesos posterior mostró que curl había borrado el valor expandido, por lo que esa lista por sí sola habría pasado por alto la exposición. Estas comprobaciones demuestran el comportamiento local del comando, no la seguridad ni el rendimiento de un proveedor.

Reproduce la prueba con el fixture local y consulta los resultados sintéticos. Solo comprueba autenticación HTTP Basic con credenciales ficticias; no prueba un servicio de proxy externo.

Una respuesta correcta demuestra que esta petición se completó; no establece un país de salida ni un operador concretos. Para eso, usa el endpoint de diagnóstico documentado por tu proveedor o un endpoint que controles y que informe de la dirección de origen observada.

Mantén el mismo destino y las mismas credenciales cuando compares tu aplicación con curl. Cambiar tres ajustes a la vez hace difícil explicar un reintento exitoso. Comparte con soporte el código de salida, los tiempos y el estado sin datos sensibles, en lugar de una traza detallada que contenga datos de autenticación.

  • Salida 5, could not resolve proxy: el nombre de host del gateway es incorrecto o el DNS local no puede verlo.
  • Salida 7, failed to connect: el host del proxy se resolvió pero el puerto está cerrado, filtrado o inalcanzable desde esta red.
  • Código 28, operation timed out: compara los límites de conexión y tiempo total con el tiempo registrado. La repetición no identifica por sí sola si la causa es el gateway, el destino o el plazo elegido.
  • connect=407: el proxy requiere autenticación. Nuestra prueba local con curl 8.7.1 devolvió el código de salida 22 con --fail y 56 sin esa opción. Lee el estado CONNECT junto con el código de salida; no exijas un 56 antes de comprobar las credenciales.
  • Salida 97, proxy handshake error: el handshake SOCKS fue rechazado; revisa el esquema socks5 frente a socks5h y las credenciales.
  • Salida 35 o 60, fallo de TLS: revisa el nombre de host, la cadena de confianza y el reloj. Mantén activada la verificación de certificados.
  • Salida 35 con wrong version number no es un problema de certificados: un handshake TLS recibió una respuesta en texto plano, del puerto del proxy o del puerto de destino. SSL WRONG_VERSION_NUMBER con proxy muestra cómo %{http_connect} distingue los dos casos.

Antes de pasar a producción

  • Usa un gateway real de tu proveedor, con el esquema y el puerto que documenta.
  • Inyecta las credenciales de forma privada; no pegues secretos en el historial del shell.
  • Registra una línea base acotada antes de probar tráfico en paralelo.
  • Anota si el fallo vino del paso CONNECT o del destino antes de cambiar ajustes.

Fuentes y lecturas adicionales

Referencias técnicas usadas para esta guía. Consulta la documentación de tu versión instalada y la configuración compatible de tu proveedor.