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 ejemplohttp://gate.proxyhat.com:8080.-ProxyCredential: objetoPSCredentialcon 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 queWebProxyclásico de .NET Framework no soporta SOCKS5 nativamente; en .NET 5+ (PowerShell 7) sí es posible medianteSocketsHttpHandler.
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
| Estrategia | Usuario del proxy | Cuándo usar | Pros | Contras |
|---|---|---|---|---|
| Rotación por petición | user-country-US (sin sesión) | Scraping masivo, SERP tracking | Máxima distribución de IPs | No mantiene sesión/login |
| Sesión pegajosa | user-session-abc123 | Login, paginación, flujos multi-paso | Mantiene cookies e IP | IP fija puede ser bloqueada |
| Sesión + geo | user-country-DE-session-xyz | Contenido localizado con sesión | Control granular | Requiere 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ámetro | Valor |
|---|---|
| Gateway HTTP | gate.proxyhat.com:8080 |
| Gateway SOCKS5 | gate.proxyhat.com:1080 |
| Formato de URL HTTP | http://USERNAME:PASSWORD@gate.proxyhat.com:8080 |
| Formato de URL SOCKS5 | socks5://USERNAME:PASSWORD@gate.proxyhat.com:1080 |
| Geo-targeting | user-country-US o user-country-DE-city-berlin |
| Sticky session | user-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,-ProxyCredentialy-ProxyUseDefaultCredentialscubren el 90% de los casos. Usa siempre-ProxyCredentialcon proxies externos. - Geo-targeting y sesiones: codifica
user-country-USyuser-session-abc123en el nombre de usuario delPSCredential. Para control fino, usaSystem.Net.WebProxy. - Sesiones persistentes:
-SessionVariabley-WebSessionmantienen cookies y headers entre peticiones, esencial para login y paginación. - Reintentos y backoff:
-MaximumRetryCounty-RetryIntervalSecmanejan errores transitorios; complementa contry/catchpara 429/503 con backoff exponencial manual. - Producción:
$env:HTTPS_PROXYpara procesos hijos,[Net.ServicePointManager]::SecurityProtocolpara TLS en PS 5.1, yForEach-Object -Parallelpara 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.






