Usar Proxies en PowerShell: Guía Práctica con Invoke-WebRequest y Invoke-RestMethod

Guía code-first para usar proxies residenciales en PowerShell con Invoke-WebRequest, Invoke-RestMethod, WebProxy, sesiones, rotación, reintentos y concurrencia. Ejemplos listos para producción.

Using Proxies in PowerShell: A Code-First Guide to Invoke-WebRequest & Invoke-RestMethod

Cómo usar proxies en PowerShell: introducción práctica

Usar proxies en PowerShell es una tarea que todo administrador de Windows, ingeniero de automatización o scripter encuentra tarde o temprano: necesitas enrutar Invoke-WebRequest o Invoke-RestMethod a través de un proxy, ya sea para evadir bloqueos de IP, para geo-targeting o para distribuir peticiones entre múltiples IPs. PowerShell incluye parámetros nativos para esto, pero la documentación oficial es escasa en ejemplos reales con proxies residenciales, sesiones pegajosas y rotación.

En esta guía vas a ver, con código ejecutable, cómo configurar Invoke-WebRequest proxy y Invoke-RestMethod proxy con ProxyHat, cómo codificar geo-targeting y sesiones en el nombre de usuario, cómo persistir cookies con WebRequestSession, cómo paginar una API JSON rotando sesiones con reintentos, y cómo llevarlo a producción con variables de entorno, TLS y concurrencia en PowerShell 7.

ProxyHat expone un gateway HTTP en gate.proxyhat.com:8080 y SOCKS5 en gate.proxyhat.com:1080. El SDK de ProxyHat usa exactamente los mismos endpoints, así que todo lo que aprendas aquí aplica también si luego integras el SDK en otros lenguajes.

Contexto técnico: por qué PowerShell necesita proxies residenciales

Cuando ejecutas Invoke-WebRequest desde tu máquina local o desde un runbook de Azure Automation, la petición sale con la IP de tu entorno. Si ese entorno está en Azure, AWS o GCP, la IP pertenece a rangos de datacenter que muchos sitios web bloquean o marcan como sospechosos. Según la documentación oficial de Microsoft, Invoke-WebRequest usa HttpClient de .NET por debajo, lo que significa que hereda toda la pila HTTP del framework, incluyendo soporte de proxies HTTP y SOCKS.

El problema es que los sitios anti-bot modernos —Cloudflare, Akamai, PerimeterX— consultan bases de datos de ASN como las que describe Wikipedia sobre sistemas autónomos y rechazan conexiones cuyo ASN corresponde a proveedores cloud. Un proxy residencial presenta una IP de un ISP doméstico real, con un ASN de operador de telecomunicaciones, lo que reduce dramáticamente la probabilidad de bloqueo. En pruebas internas con endpoints que filtran datacenters, la tasa de éxito pasa de menos del 20% con IPs de Azure a más del 95% con proxies residenciales rotativos.

1. Parámetros nativos: -Proxy, -ProxyCredential y -ProxyUseDefaultCredentials

Los cmdlets Invoke-WebRequest y Invoke-RestMethod exponen tres parámetros clave para proxies:

  • -Proxy: URL del proxy, por ejemplo http://gate.proxyhat.com:8080.
  • -ProxyCredential: objeto PSCredential con usuario y contraseña del proxy.
  • -ProxyUseDefaultCredentials: usa las credenciales del usuario de Windows actual (rara vez útil con proxies externos).

Ejemplo básico con credenciales interactivas:

# Petición simple a través de ProxyHat con credenciales interactivas
$proxyUrl = 'http://gate.proxyhat.com:8080'
$proxyCred = Get-Credential -Message 'Introduce tu usuario y contraseña de ProxyHat'

$response = Invoke-WebRequest -Uri 'https://httpbin.org/ip' `
    -Proxy $proxyUrl `
    -ProxyCredential $proxyCred `
    -UseBasicParsing

$response.Content
# { "origin": "189.45.x.x" }  ← IP residencial, no tu IP real

Si usas -ProxyUseDefaultCredentials, PowerShell enviará las credenciales de Windows del usuario actual al proxy. Eso solo sirve si el proxy acepta autenticación NTLM/Kerberos del dominio, lo cual no aplica a ProxyHat. Usa siempre -ProxyCredential.

Para Invoke-RestMethod proxy, la sintaxis es idéntica:

$proxyCred = Get-Credential

$data = Invoke-RestMethod -Uri 'https://api.github.com/repos/PowerShell/PowerShell' `
    -Proxy 'http://gate.proxyhat.com:8080' `
    -ProxyCredential $proxyCred

$data.full_name   # PowerShell/PowerShell
$data.stargazers_count

2. Geo-targeting y sesiones pegajosas en el nombre de usuario

ProxyHat permite codificar instrucciones en el nombre de usuario del proxy:

  • user-country-US: salida con IP de EE. UU.
  • user-country-DE-city-berlin: salida con IP de Berlín, Alemania.
  • user-session-abc123: sesión pegajosa — la misma IP se mantiene para todas las peticiones con ese identificador.

El reto en PowerShell es que Get-Credential pide usuario y contraseña de forma interactiva. En scripts automatizados necesitas construir un PSCredential sin prompts:

# Construir PSCredential sin prompt para geo-targeting + sticky session
$proxyUser = 'user-country-US-session-myTask42'
$proxyPass = 'tu_password_de_proxyhat'

$securePass = ConvertTo-SecureString $proxyPass -AsPlainText -Force
$proxyCred = New-Object System.Management.Automation.PSCredential($proxyUser, $securePass)

$response = Invoke-WebRequest -Uri 'https://httpbin.org/ip' `
    -Proxy 'http://gate.proxyhat.com:8080' `
    -ProxyCredential $proxyCred `
    -UseBasicParsing

$response.Content

Para control más fino sobre el HttpClient subyacente —por ejemplo, para usar SOCKS5 o ajustar el tiempo de conexión— puedes instanciar un System.Net.WebProxy directamente:

# WebProxy con control fino del HttpClient subyacente
Add-Type -AssemblyName System.Net.Http

$proxy = New-Object System.Net.WebProxy('http://gate.proxyhat.com:8080', $true)
$proxy.Credentials = New-Object System.Net.NetworkCredential('user-country-DE-city-berlin', 'tu_password')

# Usar con HttpClient directamente (PowerShell 7+)
$handler = New-Object System.Net.Http.HttpClientHandler
$handler.Proxy = $proxy
$handler.UseProxy = $true

$client = New-Object System.Net.Http.HttpClient($handler)
$client.Timeout = [TimeSpan]::FromSeconds(30)

$result = $client.GetAsync('https://httpbin.org/ip').Result
$result.Content.ReadAsStringAsync().Result

Consejo: si necesitas SOCKS5, usa socks5://gate.proxyhat.com:1080. Ten en cuenta que WebProxy clásico de .NET Framework no soporta SOCKS5 nativamente; en .NET 5+ (PowerShell 7) sí es posible mediante SocketsHttpHandler.

3. Persistir cookies y headers con WebRequestSession

Para PowerShell web scraping necesitas mantener cookies y headers entre peticiones. PowerShell ofrece -SessionVariable y -WebSession para esto:

# Primera petición: captura cookies en $session
$proxyCred = New-Object System.Management.Automation.PSCredential(
    'user-country-GB-session-login01',
    (ConvertTo-SecureString 'tu_password' -AsPlainText -Force)
)

$ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'

$first = Invoke-WebRequest -Uri 'https://httpbin.org/cookies/set?token=abc123' `
    -Proxy 'http://gate.proxyhat.com:8080' `
    -ProxyCredential $proxyCred `
    -SessionVariable session `
    -UserAgent $ua `
    -UseBasicParsing

# Segunda petición: reutiliza cookies y headers
$second = Invoke-WebRequest -Uri 'https://httpbin.org/cookies' `
    -Proxy 'http://gate.proxyhat.com:8080' `
    -ProxyCredential $proxyCred `
    -WebSession $session `
    -UserAgent $ua `
    -Headers @{ 'X-Custom-Header' = 'valor123' } `
    -UseBasicParsing

$second.Content
# { "cookies": { "token": "abc123" } }

El objeto $session es de tipo WebRequestSession y contiene:

  • $session.Cookies: contenedor de cookies persistente.
  • $session.Headers: headers que se envían en cada petición.
  • $session.UserAgent: User-Agent por defecto.

Puedes modificarlos directamente antes de la siguiente petición:

# Añadir un header de autorización persistente
$session.Headers['Authorization'] = 'Bearer mi_token_jwt'
$session.Headers['Accept-Language'] = 'es-ES,es;q=0.9,en;q=0.8'

$page2 = Invoke-WebRequest -Uri 'https://httpbin.org/headers' `
    -Proxy 'http://gate.proxyhat.com:8080' `
    -ProxyCredential $proxyCred `
    -WebSession $session `
    -UseBasicParsing

$page2.Content

4. API JSON paginada con rotación de sesiones y reintentos

Este es el caso de uso real: paginar una API JSON con Invoke-RestMethod, rotar sesiones de ProxyHat en cada página y aplicar backoff exponencial con -MaximumRetryCount y -RetryIntervalSec dentro de un bloque try/catch.

# Paginación con rotación de sesiones y reintentos
$baseProxy = 'http://gate.proxyhat.com:8080'
$proxyPass = 'tu_password'
$securePass = ConvertTo-SecureString $proxyPass -AsPlainText -Force

$ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/125.0.0.0 Safari/537.36'

$results = @()
$page = 1
$hasMore = $true

while ($hasMore) {
    # Rotar sesión por página: user-session-page1, user-session-page2, etc.
    $sessionUser = "user-country-US-session-page$page"
    $proxyCred = New-Object System.Management.Automation.PSCredential($sessionUser, $securePass)

    $uri = "https://api.ejemplo.com/items?page=$page&per_page=50"

    try {
        $data = Invoke-RestMethod -Uri $uri `
            -Proxy $baseProxy `
            -ProxyCredential $proxyCred `
            -UserAgent $ua `
            -Headers @{ 'Accept' = 'application/json' } `
            -MaximumRetryCount 3 `
            -RetryIntervalSec 5 `
            -ErrorAction Stop

        if ($data.items.Count -eq 0) {
            $hasMore = $false
        } else {
            $results += $data.items
            Write-Host "Página $page: $($data.items.Count) registros obtenidos" -ForegroundColor Green
            $page++
            Start-Sleep -Milliseconds 800  # rate limiting educado
        }
    } catch {
        $statusCode = $_.Exception.Response.StatusCode.value__
        Write-Warning "Error en página $page (HTTP $statusCode): $($_.Exception.Message)"

        if ($statusCode -eq 429 -or $statusCode -eq 503) {
            # Backoff exponencial manual para 429/503
            $backoff = [Math]::Pow(2, $page - 1) * 5
            Write-Host "Esperando $backoff segundos antes de reintentar..." -ForegroundColor Yellow
            Start-Sleep -Seconds $backoff
            continue  # no avanza $page, reintenta la misma página
        } elseif ($statusCode -eq 403) {
            Write-Error 'Bloqueo 403: considera cambiar de país o usar proxy móvil.'
            break
        } else {
            Write-Error "Error no recuperable: $($_.Exception.Message)"
            break
        }
    }
}

Write-Host "Total de registros: $($results.Count)"

Los parámetros -MaximumRetryCount y -RetryIntervalSec manejan reintentos automáticos para errores transitorios (HTTP 429, 500, 502, 503), pero no cubren todos los escenarios. El bloque try/catch con backoff manual complementa para casos como 429 con cabecera Retry-After o cuando necesitas cambiar de sesión antes de reintentar.

Comparación: rotación por petición vs sesión pegajosa

EstrategiaUsuario del proxyCuándo usarProsContras
Rotación por peticiónuser-country-US (sin sesión)Scraping masivo, SERP trackingMáxima distribución de IPsNo mantiene sesión/login
Sesión pegajosauser-session-abc123Login, paginación, flujos multi-pasoMantiene cookies e IPIP fija puede ser bloqueada
Sesión + geouser-country-DE-session-xyzContenido localizado con sesiónControl granularRequiere gestión de IDs

5. Consejos de producción

Variables de entorno para procesos hijos

Si tu script de PowerShell lanza procesos hijos (por ejemplo, curl, git o herramientas de línea de comandos), estos no heredan -Proxy. Establece variables de entorno:

# Variables de entorno para procesos hijos
$env:HTTP_PROXY  = 'http://gate.proxyhat.com:8080'
$env:HTTPS_PROXY = 'http://gate.proxyhat.com:8080'
$env:NO_PROXY    = 'localhost,127.0.0.1,.internal.corp'

# Ahora curl.exe usa el proxy automáticamente
curl.exe -s 'https://httpbin.org/ip'

Para autenticación en variables de entorno, puedes incluir credenciales en la URL: http://user-country-US:tu_password@gate.proxyhat.com:8080. Ten en cuenta que esto expone la contraseña en texto plano en el entorno del proceso; úsalo solo en entornos controlados.

TLS y compatibilidad de protocolos

Algunos endpoints requieren TLS 1.2 o 1.3. En Windows PowerShell 5.1 (que usa .NET Framework), el protocolo por defecto puede ser TLS 1.0. Fuerza TLS 1.2:

# Forzar TLS 1.2 en PowerShell 5.1
[Net.ServicePointManager]::SecurityProtocol =
    [Net.SecurityProtocolType]::Tls12

# En PowerShell 7+ (.NET Core), esto no es necesario
# porque SocketsHttpHandler negocia automáticamente

Según la documentación de Microsoft sobre SecurityProtocolType, Tls12 equivale a TLS 1.2 y Tls13 a TLS 1.3 (disponible en .NET 4.8+ y .NET Core 3.0+).

Concurrencia con ForEach-Object -Parallel (PowerShell 7)

Para scraping a escala, usa ForEach-Object -Parallel con un límite de simultaneidad. Cada thread debe tener su propio PSCredential y su propio identificador de sesión:

# Scraping concurrente con PowerShell 7
$urls = 1..50 | ForEach-Object { "https://api.ejemplo.com/items?page=$_&per_page=100" }
$proxyPass = 'tu_password'
$securePass = ConvertTo-SecureString $proxyPass -AsPlainText -Force

$results = $urls | ForEach-Object -ThrottleLimit 10 -Parallel {
    $page = $_ -replace '.*page=(\d+).*','$1'
    $sessionUser = "user-country-US-session-parallel-$page"
    $cred = New-Object System.Management.Automation.PSCredential($sessionUser, $using:securePass)

    try {
        $data = Invoke-RestMethod -Uri $_ `
            -Proxy 'http://gate.proxyhat.com:8080' `
            -ProxyCredential $cred `
            -MaximumRetryCount 3 `
            -RetryIntervalSec 3 `
            -ErrorAction Stop

        [PSCustomObject]@{
            Page   = [int]$page
            Count  = $data.items.Count
            Status = 'OK'
        }
    } catch {
        [PSCustomObject]@{
            Page   = [int]$page
            Count  = 0
            Status = "Error: $($_.Exception.Message)"
        }
    }
}

$results | Format-Table -AutoSize

El parámetro -ThrottleLimit 10 mantiene 10 peticiones concurrentes. Con proxies residenciales de ProxyHat puedes subir a 50 o 100 conexiones concurrentes sin problemas, pero mantén -ThrottleLimit por debajo del límite de tu plan para evitar errores de autenticación.

Logging y circuit breakers

# Circuit breaker simple para detener tras N errores consecutivos
$ErrorActionPreference = 'Stop'
$maxConsecutiveErrors = 5
$consecutiveErrors = 0
$allResults = @()

foreach ($page in 1..100) {
    $sessionUser = "user-country-US-session-cb-$page"
    $cred = New-Object System.Management.Automation.PSCredential($sessionUser, $securePass)

    try {
        $data = Invoke-RestMethod -Uri "https://api.ejemplo.com/items?page=$page" `
            -Proxy 'http://gate.proxyhat.com:8080' `
            -ProxyCredential $cred `
            -MaximumRetryCount 3 `
            -RetryIntervalSec 5

        $allResults += $data.items
        $consecutiveErrors = 0
        Write-Information "[$(Get-Date -Format 'HH:mm:ss')] Página $page OK" -InformationAction Continue
    } catch {
        $consecutiveErrors++
        Write-Warning "[$(Get-Date -Format 'HH:mm:ss')] Error en página $page ($consecutiveErrors consecutivos)"

        if ($consecutiveErrors -ge $maxConsecutiveErrors) {
            Write-Error 'Circuit breaker abierto: demasiados errores consecutivos. Deteniendo.'
            break
        }
        Start-Sleep -Seconds ([Math]::Pow(2, $consecutiveErrors))
    }
}

6. Consideraciones éticas y legales

El scraping de datos públicos es legal en muchas jurisdicciones, pero hay límites importantes:

  • CFAA (EE. UU.): la Computer Fraud and Abuse Act prohíbe el acceso no autorizado a sistemas. Los tribunales han aclarado que scraping datos públicos accesibles sin login generalmente no viola la CFAA (caso hiQ Labs v. LinkedIn, 2022), pero eludir barreras técnicas como CAPTCHAs o autenticación puede cruzar la línea.
  • GDPR (UE): el Reglamento General de Protección de Datos aplica a datos personales de residentes de la UE. Si tu scraping recolecta datos personales, necesitas una base legal. La Comisión Europea ofrece guía oficial sobre el tema.
  • robots.txt: respétalo. Aunque no es legalmente vinculante en todas las jurisdicciones, ignorarlo debilita tu posición si hay un litigio.
  • Términos de servicio: revisa los ToS del sitio. Algunos prohíben explícitamente el scraping automatizado.
  • APIs oficiales primero: si el sitio ofrece una API pública o de pago, úsala. Es más estable, más rápida y legalmente más segura que el scraping.

Para casos de uso como web scraping y SERP tracking, los proxies residenciales de ProxyHat te dan la infraestructura de IPs; la responsabilidad de qué datos recoges y cómo los usas es tuya.

Configuración de ProxyHat

Para empezar, necesitas una cuenta en ProxyHat. Los parámetros de conexión son:

ParámetroValor
Gateway HTTPgate.proxyhat.com:8080
Gateway SOCKS5gate.proxyhat.com:1080
Formato de URL HTTPhttp://USERNAME:PASSWORD@gate.proxyhat.com:8080
Formato de URL SOCKS5socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080
Geo-targetinguser-country-US o user-country-DE-city-berlin
Sticky sessionuser-session-abc123

Consulta la documentación oficial de ProxyHat para detalles sobre planes, límites de concurrencia y ubicaciones disponibles.

Conclusiones clave

Key Takeaways

  • Parámetros nativos: -Proxy, -ProxyCredential y -ProxyUseDefaultCredentials cubren el 90% de los casos. Usa siempre -ProxyCredential con proxies externos.
  • Geo-targeting y sesiones: codifica user-country-US y user-session-abc123 en el nombre de usuario del PSCredential. Para control fino, usa System.Net.WebProxy.
  • Sesiones persistentes: -SessionVariable y -WebSession mantienen cookies y headers entre peticiones, esencial para login y paginación.
  • Reintentos y backoff: -MaximumRetryCount y -RetryIntervalSec manejan errores transitorios; complementa con try/catch para 429/503 con backoff exponencial manual.
  • Producción: $env:HTTPS_PROXY para procesos hijos, [Net.ServicePointManager]::SecurityProtocol para TLS en PS 5.1, y ForEach-Object -Parallel para concurrencia en PS 7.
  • Ética: usa solo datos públicos, respeta robots.txt, revisa ToS, prioriza APIs oficiales y cumple con CFAA y GDPR.

Con estos patrones tienes todo lo necesario para integrar ProxyHat en scripts de PowerShell de forma robusta. Empieza con una petición simple, añade sesiones, después rotación y finalmente concurrencia. Cada capa resuelve un problema concreto sin añadir complejidad innecesaria.

¿Listo para empezar?

Accede a más de 50M de IPs residenciales en más de 148 países con filtrado impulsado por IA.

Ver preciosProxies residenciales
← Volver al Blog