Si administras servidores, escribes scripts de automatización o despliegas aplicaciones en infraestructura corporativa, tarde o temprano te encontrarás con la necesidad de enviar tráfico de curl o wget a través de un proxy. Esto puede ser un proxy corporativo, eludir bloqueos geográficos al descargar paquetes, o rotar IPs al hacer solicitudes masivas a APIs externas. En este artículo, solo hay práctica: comandos, configuraciones, ejemplos de código sin rodeos.
1. Cómo funcionan curl y wget con proxies: mecanismos básicos
Antes de meterse en las configuraciones, es importante entender qué sucede bajo el capó. Ambas herramientas soportan dos protocolos de proxy principales: HTTP/HTTPS y SOCKS5. La mecánica es diferente, y esto afecta qué tipo de proxy elegir para una tarea específica.
Proxy HTTP actúa como intermediario a nivel de protocolo de aplicación. Cuando curl envía una solicitud a través de un proxy HTTP, le dice literalmente al servidor proxy: "haz una solicitud GET a esta URL en lugar de mí". Para el tráfico HTTPS se utiliza el método CONNECT: curl le pide al proxy que establezca un túnel hasta el host de destino, después de lo cual el apretón de manos TLS ocurre directamente entre el cliente y el servidor de destino. Esto es importante: el proxy en este caso no ve el contenido del tráfico HTTPS.
SOCKS5 opera a un nivel más bajo: proxy TCP/UDP sin estar atado a un protocolo de aplicación. Esto hace que SOCKS5 sea más versátil: puede manejar no solo HTTP/HTTPS, sino también otros protocolos. Además, SOCKS5 soporta la resolución DNS del lado del servidor proxy, lo cual es crítico para prevenir fugas de DNS.
💡 Diferencia clave para la práctica:
Si solo necesitas descargar un archivo o hacer una solicitud API, un proxy HTTP es suficiente. Si necesitas total anonimato, eludir fugas de DNS o trabajar con protocolos no estándar, utiliza SOCKS5.
curl soporta ambos protocolos de manera nativa desde versiones muy tempranas. wget históricamente ha tenido un soporte más limitado para SOCKS5: en versiones antiguas (hasta 1.19) no hay soporte para SOCKS5 en absoluto, solo para proxies HTTP. Esto debe tenerse en cuenta al escribir scripts que deben funcionar en diferentes distribuciones.
Puedes verificar la versión de wget con el comando wget --version. Para curl, usa curl --version, allí también podrás ver con qué protocolos se compiló la biblioteca libcurl.
2. Variables de entorno: la forma más rápida de configurar
La forma más elegante de configurar un proxy para curl y wget es a través de variables de entorno. Esto funciona a nivel del sistema: no necesitas cambiar cada script, solo establece las variables una vez en la sesión o agrégalas al perfil del usuario.
Ambas herramientas leen las mismas variables estándar:
# Para tráfico HTTP export http_proxy="http://proxy.example.com:3128" # Para tráfico HTTPS export https_proxy="http://proxy.example.com:3128" # Duplicamos en mayúsculas (algunos programas solo leen esto) export HTTP_PROXY="http://proxy.example.com:3128" export HTTPS_PROXY="http://proxy.example.com:3128" # Para FTP (si es necesario) export ftp_proxy="http://proxy.example.com:3128" # Excepciones — direcciones que NO pasan por el proxy export no_proxy="localhost,127.0.0.1,::1,192.168.0.0/16,.internal.company.com"
Un matiz importante: curl es sensible a las mayúsculas de las variables dependiendo de la versión. Para evitar sorpresas, siempre establece ambas versiones: en minúsculas y mayúsculas. Esta es una práctica estándar en DevOps.
Para que la configuración se aplique de manera constante para un usuario específico, agrega las líneas a ~/.bashrc o ~/.profile. Para scripts y servicios del sistema, en /etc/environment o en el archivo de unidad de systemd a través de la directiva Environment=.
# /etc/systemd/system/myservice.service
[Service]
Environment="http_proxy=http://proxy.example.com:3128"
Environment="https_proxy=http://proxy.example.com:3128"
Environment="no_proxy=localhost,127.0.0.1"
ExecStart=/usr/bin/myapp
Si el proxy requiere autenticación, el usuario y la contraseña se insertan directamente en la URL de la variable:
export http_proxy="http://username:[email protected]:3128" export https_proxy="http://username:[email protected]:3128"
⚠️ Seguridad:
No almacenes contraseñas en texto claro en .bashrc o en variables de entorno en servidores de producción. Utiliza soluciones de vault (HashiCorp Vault, AWS Secrets Manager) o al menos limita los permisos del archivo con las variables.
3. Flags de curl para trabajar con proxies: análisis completo
curl proporciona un rico conjunto de flags para gestionar proxies directamente desde la línea de comandos. Esto es conveniente cuando necesitas hacer una solicitud única a través de un proxy sin cambiar la configuración del sistema.
El flag principal es -x o --proxy:
# Proxy HTTP básico curl -x http://proxy.example.com:3128 https://api.example.com/data # Forma corta curl -x proxy.example.com:3128 https://api.example.com/data # Con especificación explícita del protocolo curl --proxy http://proxy.example.com:3128 https://api.example.com/data # Verificar IP externa a través del proxy curl -x http://proxy.example.com:3128 https://api.ipify.org
Para ignorar las variables de entorno y conectarse directamente (eludir el proxy configurado), se utiliza el flag --noproxy:
# Ignorar proxy para un host específico curl --noproxy "internal.company.com" https://internal.company.com/api # Ignorar proxy completamente (incluso si se establecen variables de entorno) curl --noproxy "*" https://api.example.com/data
Flags útiles para depurar conexiones proxy:
# Modo verbose: se ven todos los encabezados, incluyendo CONNECT al proxy curl -v -x http://proxy.example.com:3128 https://api.example.com/data # Solo encabezados de respuesta curl -I -x http://proxy.example.com:3128 https://api.example.com/data # Mostrar tiempo de conexión a través del proxy curl -w "Connect: %{time_connect}s\nTotal: %{time_total}s\n" \ -x http://proxy.example.com:3128 \ -o /dev/null -s https://api.example.com/data
Configurar el proxy a través del archivo de configuración ~/.curlrc es conveniente si no quieres escribir los flags cada vez:
# ~/.curlrc
proxy = http://proxy.example.com:3128
proxy-user = username:password
noproxy = localhost,127.0.0.1,.internal.company.com
Para scripts del sistema, puedes crear un archivo de configuración separado y especificarlo explícitamente a través de curl -K /path/to/config — esto permite tener diferentes perfiles de proxy para diferentes tareas.
4. Configuración de proxy en wget: flags y archivo de configuración
wget es menos flexible que curl, pero para tareas típicas — descargar archivos, espejar sitios recursivamente — sus capacidades son más que suficientes. El proxy en wget se puede configurar de tres maneras: a través de variables de entorno (ya discutidas), a través de flags de línea de comandos y a través del archivo de configuración ~/.wgetrc.
Flags de línea de comandos de wget para proxies:
# Proxy HTTP a través del flag wget -e use_proxy=yes \ -e http_proxy=http://proxy.example.com:3128 \ https://example.com/file.tar.gz # HTTPS a través del proxy wget -e use_proxy=yes \ -e https_proxy=http://proxy.example.com:3128 \ https://example.com/file.tar.gz # Con autenticación wget -e use_proxy=yes \ -e http_proxy=http://username:[email protected]:3128 \ https://example.com/file.tar.gz # Desactivar proxy para un comando específico (si se establecen variables de entorno) wget --no-proxy https://internal.company.com/file.tar.gz
El archivo de configuración ~/.wgetrc es la forma preferida para configuraciones permanentes:
# ~/.wgetrc use_proxy = on http_proxy = http://proxy.example.com:3128 https_proxy = http://proxy.example.com:3128 ftp_proxy = http://proxy.example.com:3128 no_proxy = localhost,127.0.0.1,.internal.company.com # Si el proxy requiere autenticación proxy_user = username proxy_password = secretpassword
Para aplicación a nivel del sistema (todos los usuarios), se utiliza /etc/wgetrc — el mismo formato, pero se aplica globalmente. Conveniente para servidores donde todas las operaciones de descarga deben ir a través de un proxy corporativo.
Un ejemplo práctico: descarga recursiva de un sitio a través de un proxy con limitación de profundidad y velocidad:
wget -e use_proxy=yes \
-e http_proxy=http://proxy.example.com:3128 \
--recursive \
--level=2 \
--limit-rate=500k \
--wait=1 \
--random-wait \
--user-agent="Mozilla/5.0 (compatible; Googlebot/2.1)" \
https://example.com/docs/
5. Proxy SOCKS5 en curl y wget: configuración y ejemplos
SOCKS5 es un protocolo más preferido para tareas donde la anonimidad o el trabajo con puertos no estándar son importantes. Para administradores de sistemas e ingenieros de DevOps, SOCKS5 se utiliza a menudo al trabajar a través de túneles SSH, así como al conectarse a proxies residenciales, que simulan el tráfico de usuarios reales.
En curl, SOCKS5 se soporta a través de un prefijo especial en la URL del proxy:
# SOCKS5 con resolución DNS del lado del cliente (¡puede haber fuga de DNS!) curl --socks5 proxy.example.com:1080 https://api.example.com/data # SOCKS5 con resolución DNS del lado del proxy (¡recomendado!) curl --socks5-hostname proxy.example.com:1080 https://api.example.com/data # A través del flag -x con especificación explícita del protocolo curl -x socks5h://proxy.example.com:1080 https://api.example.com/data # Con autenticación curl -x socks5h://username:[email protected]:1080 https://api.example.com/data # SOCKS5 a través de variable de entorno export all_proxy="socks5h://proxy.example.com:1080" curl https://api.example.com/data
📌 socks5 vs socks5h — ¿cuál es la diferencia?
socks5 — la consulta DNS se realiza localmente, solo se establece la conexión TCP a través del proxy por IP. Puede haber fuga de DNS.socks5h (h = hostname) — la consulta DNS se realiza en el lado del servidor proxy. Total anonimato. Recomendado para la mayoría de las tareas.
Un escenario popular en DevOps es utilizar SSH como proxy SOCKS5 para tunelizar tráfico a través de un host bastión:
# Abrimos un túnel SSH con SOCKS5 en el puerto local 1080 ssh -D 1080 -f -C -q -N [email protected] # Ahora usamos este túnel en curl curl -x socks5h://localhost:1080 https://internal-api.private.network/data # O a través de variable de entorno para todas las solicitudes en la sesión export all_proxy="socks5h://localhost:1080" wget https://internal-resource.private.network/file.tar.gz
wget soporta SOCKS5 desde la versión 1.19. En versiones más antiguas (CentOS 7, Ubuntu 16.04), tendrás que usar soluciones alternativas: proxychains, tsocks, o cambiar a curl para tareas que requieren SOCKS5.
6. Autenticación en el proxy: usuario y contraseña
La mayoría de los proxies comerciales y servidores proxy corporativos requieren autenticación. Hay varias formas de pasar las credenciales, cada una con sus pros y contras en términos de seguridad.
Método 1: Credenciales en la URL — simple, pero inseguro (la contraseña es visible en la lista de procesos):
curl -x http://user:p%[email protected]:3128 https://api.example.com/data # Los caracteres especiales en la contraseña deben ser codificados en URL: @ → %40, : → %3A
Método 2: Flag --proxy-user en curl — la contraseña no necesita ser especificada en la línea de comandos, curl la solicitará interactivamente:
# Usuario y contraseña en el flag (aún visible en ps aux) curl -x http://proxy.example.com:3128 \ --proxy-user "username:password" \ https://api.example.com/data # Solo usuario — curl preguntará por la contraseña interactivamente curl -x http://proxy.example.com:3128 \ --proxy-user "username" \ https://api.example.com/data
Método 3: A través del archivo .netrc — el más seguro para scripts:
# ~/.netrc machine proxy.example.com login username password secretpassword # Limitar los permisos del archivo chmod 600 ~/.netrc # Usar en curl curl -x http://proxy.example.com:3128 --proxy-netrc https://api.example.com/data
Método 4: A través de variables de entorno desde un almacén secreto — recomendado para CI/CD y entornos de producción:
# En el script leemos las credenciales de las variables (que establece CI/CD)
#!/bin/bash
PROXY_URL="http://${PROXY_USER}:${PROXY_PASS}@proxy.example.com:3128"
curl -x "${PROXY_URL}" https://api.example.com/data
Tabla comparativa de métodos de autenticación:
| Método | Comodidad | Seguridad | Adecuado para |
|---|---|---|---|
| URL (user:pass@host) | ⭐⭐⭐ | ⭐ | Pruebas |
| Flag --proxy-user | ⭐⭐⭐ | ⭐⭐ | Comandos únicos |
| Archivo .netrc | ⭐⭐ | ⭐⭐⭐ | Scripts locales |
| Variables de entorno de vault | ⭐⭐ | ⭐⭐⭐⭐⭐ | CI/CD, producción |
7. Excepciones y no_proxy: cómo eludir el proxy para direcciones locales
En entornos corporativos y en la nube, a menudo se necesita una configuración fina: tráfico externo — a través del proxy, interno — directamente. La variable no_proxy (o NO_PROXY) permite establecer una lista de excepciones.
# Excepciones básicas export no_proxy="localhost,127.0.0.1,::1" # Excepción por dominio (con punto — todos los subdominios) export no_proxy="localhost,127.0.0.1,.internal.company.com,.corp.local" # Excepción por rango de IP (¡la notación CIDR no funciona en todas partes!) export no_proxy="localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" # Para AWS: excluimos el endpoint de metadatos y direcciones internas export no_proxy="localhost,127.0.0.1,169.254.169.254,.amazonaws.com.internal"
⚠️ Característica importante de no_proxy:
La notación CIDR (10.0.0.0/8) no es soportada por todas las herramientas. curl la soporta desde la versión 7.86.0. wget — no la soporta en absoluto. Para compatibilidad, es mejor enumerar IPs específicas o máscaras como 10. (todo lo que comienza con 10.).
Un ejemplo práctico para un entorno Kubernetes, donde se necesita excluir el servidor API y servicios internos:
export no_proxy="localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,.cluster.local,.svc,.default"
export NO_PROXY="${no_proxy}"
8. Proxy en CI/CD: GitHub Actions, GitLab CI, Docker
Configurar proxies en pipelines de CI/CD es una de las tareas más comunes para ingenieros de DevOps que trabajan en redes corporativas o con acceso limitado a Internet. Analicemos configuraciones específicas para plataformas populares.
GitHub Actions
# .github/workflows/deploy.yml
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
env:
http_proxy: ${{ secrets.PROXY_URL }}
https_proxy: ${{ secrets.PROXY_URL }}
no_proxy: "localhost,127.0.0.1,.github.com"
steps:
- uses: actions/checkout@v3
- name: Download dependencies
run: |
curl -x "${http_proxy}" https://external-api.example.com/config.json -o config.json
wget -e use_proxy=yes -e http_proxy="${http_proxy}" https://releases.example.com/app-v1.0.tar.gz
GitLab CI
# .gitlab-ci.yml
variables:
http_proxy: "http://proxy.company.com:3128"
https_proxy: "http://proxy.company.com:3128"
no_proxy: "localhost,127.0.0.1,.gitlab.company.com"
build:
stage: build
script:
- curl -x "${http_proxy}" https://registry.npmjs.org/package -o package.json
- wget -e use_proxy=yes -e http_proxy="${http_proxy}" https://example.com/resource.tar.gz
Docker: proxy al construir imágenes
# Pasar el proxy a través de argumentos de construcción docker build \ --build-arg http_proxy=http://proxy.example.com:3128 \ --build-arg https_proxy=http://proxy.example.com:3128 \ --build-arg no_proxy=localhost,127.0.0.1 \ -t myapp:latest . # En Dockerfile usamos ARG para obtener las variables
# Dockerfile FROM ubuntu:22.04 ARG http_proxy ARG https_proxy ARG no_proxy ENV http_proxy=${http_proxy} ENV https_proxy=${https_proxy} ENV no_proxy=${no_proxy} RUN apt-get update && apt-get install -y curl wget RUN curl -x "${http_proxy}" https://example.com/setup.sh | bash # Limpiamos las variables de proxy en la imagen final (opcional) ENV http_proxy="" ENV https_proxy=""
Configuración global de proxy para el daemon de Docker (para docker pull a través del proxy):
# /etc/systemd/system/docker.service.d/proxy.conf [Service] Environment="HTTP_PROXY=http://proxy.example.com:3128" Environment="HTTPS_PROXY=http://proxy.example.com:3128" Environment="NO_PROXY=localhost,127.0.0.1,.internal.registry.com" # Reiniciamos docker systemctl daemon-reload systemctl restart docker
9. Rotación de proxies en scripts bash
Si necesitas hacer una gran cantidad de solicitudes a APIs externas o servicios, la rotación de proxies permite distribuir la carga y evitar bloqueos por IP. Esto es especialmente relevante al monitorear precios, recopilar datos o probar la disponibilidad de recursos desde diferentes regiones.
Para tales tareas, los proxies de centros de datos son muy adecuados, ya que ofrecen alta velocidad y estabilidad en solicitudes masivas.
#!/bin/bash # rotate_proxy.sh — rotación de proxies desde una lista PROXY_LIST=( "http://user:[email protected]:3128" "http://user:[email protected]:3128" "http://user:[email protected]:3128" "http://user:[email protected]:3128" ) URLS_FILE="urls.txt" OUTPUT_DIR="./results" mkdir -p "${OUTPUT_DIR}" PROXY_COUNT=${#PROXY_LIST[@]} INDEX=0 while IFS= read -r url; do PROXY="${PROXY_LIST[$INDEX]}" FILENAME=$(echo "${url}" | md5sum | cut -d' ' -f1) echo "Solicitando: ${url} a través del proxy $((INDEX + 1))/${PROXY_COUNT}" curl -x "${PROXY}" \ --max-time 30 \ --retry 3 \ --retry-delay 2 \ --silent \ --output "${OUTPUT_DIR}/${FILENAME}.html" \ "${url}" if [ $? -eq 0 ]; then echo " ✓ Éxito" else echo " ✗ Falló, intentando con el siguiente proxy..." INDEX=$(( (INDEX + 1) % PROXY_COUNT )) curl -x "${PROXY_LIST[$INDEX]}" \ --max-time 30 \ --silent \ --output "${OUTPUT_DIR}/${FILENAME}.html" \ "${url}" fi # Pasamos al siguiente proxy INDEX=$(( (INDEX + 1) % PROXY_COUNT )) # Pequeña pausa entre solicitudes sleep 0.5 done < "${URLS_FILE}" echo "¡Hecho! Resultados guardados en ${OUTPUT_DIR}"
Una variante más avanzada es verificar la disponibilidad del proxy antes de usarlo:
#!/bin/bash # check_proxy.sh — verificación de disponibilidad del proxy check_proxy() { local proxy_url="$1" local test_url="https://api.ipify.org" result=$(curl -x "${proxy_url}" \ --max-time 10 \ --silent \ --write-out "%{http_code}" \ --output /dev/null \ "${test_url}") if [ "${result}" -eq 200 ]; then echo "VIVO" else echo "MUERTO" fi } # Obtenemos la IP externa a través del proxy get_proxy_ip() { local proxy_url="$1" curl -x "${proxy_url}" --max-time 10 --silent https://api.ipify.org } PROXY="http://user:[email protected]:3128" STATUS=$(check_proxy "${PROXY}") echo "Estado del proxy: ${STATUS}" if [ "${STATUS}" == "VIVO" ]; then EXTERNAL_IP=$(get_proxy_ip "${PROXY}") echo "IP externa a través del proxy: ${EXTERNAL_IP}" fi
10. Depuración y errores comunes
Trabajar con proxies inevitablemente conlleva errores, especialmente durante la configuración inicial. Analicemos los problemas más comunes y cómo diagnosticarlos.
Diagnóstico con modo verbose
# Salida de curl lo más detallada posible curl -vvv -x http://proxy.example.com:3128 https://api.example.com/data 2>&1 | head -50 # Qué observar en la salida: # * Conectado a proxy.example.com — conexión con el proxy establecida # CONNECT api.example.com:443 — solicitud de túnel # HTTP/1.1 200 Connection established — túnel abierto # * SSL connection using TLS — TLS funciona # Depuración de wget wget -d -e use_proxy=yes -e http_proxy=http://proxy.example.com:3128 https://api.example.com/data
Errores comunes y soluciones
| Error | Causa | Solución |
|---|---|---|
407 Proxy Authentication Required |
No se proporcionaron credenciales | Agregar user:pass en la URL del proxy o el flag --proxy-user |
Connection refused |
Puerto incorrecto o proxy no disponible | Verificar el puerto: nc -zv proxy.host 3128 |
SSL certificate error |
Proxy corporativo con inspección SSL | Agregar CA corporativa: --cacert /path/to/ca.crt |
Could not resolve proxy |
DNS no resuelve el nombre del proxy | Usar IP en lugar de nombre o verificar DNS |
Timeout |
Proxy lento o sobrecargado | Aumentar el tiempo de espera: --max-time |