Configurar proxy en PowerShell: Invoke-WebRequest con proxy

Publicado:

16 min de lectura

Acar Diveroli
Autor: Acar Diveroli
Línea Invoke-WebRequest -Proxy con cursor azul, traza de la petición por el proxy, StatusCode : 200 y sello de IP de salida

Un script que funcionaba sin problemas en tu portátil pasa a un servidor, o ahora tiene que leer una lista de precios como un visitante de otro país, y cada llamada a Invoke-WebRequest debe salir por un proxy. PowerShell tiene parámetros para eso. Lo que pasa es que se comportan de forma distinta en Windows PowerShell 5.1 y en PowerShell 7, y el atajo más habitual, escribir user:pass@ dentro de la dirección del proxy, falla con un 407 en 5.1 sin decir por qué.

Este artículo cubre -Proxy y -ProxyCredential, las credenciales sin cuadro de diálogo, un proxy para todas las llamadas, las variables de entorno y el proxy del sistema de Windows, SOCKS5, un script de descarga con reintentos y los textos de error exactos. Cada fragmento para 5.1 se ejecutó en Windows PowerShell 5.1 (compilación 26100) contra un proxy local que exige usuario y contraseña. PowerShell 7 no estaba instalado en el equipo de pruebas, así que su comportamiento viene de la documentación de Microsoft y se indica como tal.

Windows PowerShell 5.1 o PowerShell 7: ¿por qué importa?

Las dos versiones envían las peticiones web por pilas HTTP distintas, así que la misma configuración de proxy puede funcionar en una e ignorarse en la otra. Windows PowerShell 5.1 viene con Windows y usa la clase HttpWebRequest de .NET Framework; PowerShell 7 se descarga aparte y usa HttpClient. $PSVersionTable.PSVersion te dice con cuál se ejecuta un script.

ComportamientoWindows PowerShell 5.1PowerShell 7.x
-Proxy, -ProxyCredentialSíSí
HTTP_PROXY, HTTPS_PROXY, NO_PROXYSe ignoranSe leen desde 7.0
Proxy cuando no se indica ningunoOpciones de Internet del usuarioVariables, luego ajustes del usuario
Modificador -NoProxyNoDesde 6.0
Dirección SOCKS5No7.2 y posteriores
Alias curl y wgetApuntan a Invoke-WebRequestEliminados
Reintentos integradosNo-MaximumRetryCount

La columna de 5.1 es lo que observamos; la de 7.x sigue la referencia de Invoke-WebRequest para PowerShell 7.5.

¿Cómo envía Invoke-WebRequest una petición a través de un proxy?

  1. PowerShell resuelve el nombre de host del proxy, no el del destino, y abre una conexión TCP al puerto del proxy.
  2. Para un destino http:// envía la URL completa al proxy (GET http://httpbin.org/ip HTTP/1.1). El proxy descarga la página y la reenvía, así que puede leer la petición.
  3. Para un destino https:// envía CONNECT httpbin.org:443. Cuando el proxy responde 200 Connection Established, el handshake TLS con el sitio va dentro de ese túnel; el proxy solo ve el nombre de host y el puerto.
  4. Las credenciales salen solo después de un desafío. El primer intento no lleva cabecera Proxy-Authorization, el proxy responde 407 con Proxy-Authenticate: Basic y .NET repite la petición con la cabecera. El código de estado 407 y el desafío están definidos en la RFC 9110; 5.1 no se autentica por adelantado, así que nuestro proxy de prueba registró cada llamada correcta como este par:
text
11:02:56 407 GET http://httpbin.org/ip (no Proxy-Authorization)
11:02:56 GET http://httpbin.org/ip user=user
  1. El destino responde a la IP de salida del proxy. El cmdlet devuelve un objeto con StatusCode, Headers y Content; en 5.1, un estado que no sea 2xx se convierte en un error terminal.

¿Cómo se usa Invoke-WebRequest con un proxy?

Crea un objeto de credenciales y pasa al cmdlet la dirección del proxy y el objeto:

powershell
$pass = ConvertTo-SecureString "pass" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("user", $pass)

$r = Invoke-WebRequest -UseBasicParsing -Uri "https://httpbin.org/ip" `
    -Proxy "http://pr.proxynet.io:8000" -ProxyCredential $cred
$r | Select-Object StatusCode, StatusDescription | Format-List
$r.Content
text
StatusCode        : 200
StatusDescription : OK

{
  "origin": "203.0.113.42"
}

httpbin.org/ip devuelve la dirección desde la que llegó la petición, así que un proxy que funciona muestra ahí su IP de salida en lugar de la tuya. El valor de -Proxy es un URI y conserva el esquema http:// incluso con destinos HTTPS: describe la conexión con el proxy, mientras la página sigue cifrada dentro del túnel CONNECT. Los dos parámetros están documentados en la página de Invoke-WebRequest para Windows PowerShell 5.1.

Añade siempre -UseBasicParsing en 5.1. Sin él, el cmdlet analiza el HTML con el motor de Internet Explorer y, desde la actualización de seguridad de Microsoft de diciembre de 2025 para CVE-2025-54100, pide confirmación antes, algo que un script programado no puede responder. En nuestra ejecución, una llamada sin el modificador falló con Object reference not set to an instance of an object. PowerShell 7 solo hace análisis básico e ignora el modificador.

Invoke-RestMethod acepta los mismos parámetros de proxy y devuelve JSON analizado en lugar de un objeto de respuesta:

powershell
$ip = Invoke-RestMethod -Uri "https://api.ipify.org?format=json" `
    -Proxy "http://pr.proxynet.io:8000" -ProxyCredential $cred
$ip.ip

En los productos residenciales y móviles de Proxynet, el nombre de usuario lleva el país, la ciudad y la sesión elegidos en el Generador de endpoints del panel, así que el comando no cambia elijas la salida que elijas.

¿Cómo se pasan las credenciales del proxy?

La contraseña del proxy viaja en un objeto PSCredential, nunca dentro de la dirección. Get-Credential la pide de forma interactiva; un script la construye a partir de una cadena segura, como arriba. La página de Microsoft sobre ConvertTo-SecureString advierte de que una contraseña en texto plano en la línea de comandos acaba en los registros de eventos y en el historial de comandos, así que léela de una variable de entorno o de un almacén de secretos:

powershell
$pass = ConvertTo-SecureString $env:PROXY_PASS -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($env:PROXY_USER, $pass)

Tres detalles de nuestras pruebas:

  • user:pass@ dentro de -Proxy se ignora en 5.1. -Proxy "http://user:pass@pr.proxynet.io:8000" sin -ProxyCredential terminó con (407) Proxy Authentication Required; el registro del proxy no mostró ninguna credencial.
  • Los caracteres especiales no necesitan codificación. Una contraseña p@ss:w#rd pasó sin cambios a través de PSCredential, donde la forma de URL habría necesitado %40, %3A y %23.
  • Una sesión web conserva el proxy. Tras -Proxy ... -SessionVariable s, una segunda llamada solo con -WebSession $s volvió a pasar por el proxy; el objeto de sesión guarda el proxy junto a las cookies.

-ProxyUseDefaultCredentials envía en su lugar la cuenta de Windows del usuario actual, para proxies corporativos que autentican contra el dominio; la documentación dice que no se puede combinar con -ProxyCredential. No lo probamos aquí: nuestro proxy solo habla Basic.

La contraseña desaparece del todo cuando la IP del equipo está autorizada en el panel del proxy: -Proxy "http://pr.proxynet.io:8000" es entonces toda la configuración. Proxynet acepta hasta 10 IP en la lista de IP autorizadas, lo que encaja con tareas programadas en un servidor con dirección fija.

¿Cómo se configura el proxy para todos los comandos?

$PSDefaultParameterValues es una tabla hash de claves Cmdlet:Parámetro que PowerShell aplica siempre que omites el parámetro. Bastan dos entradas por cmdlet:

powershell
$PSDefaultParameterValues["Invoke-WebRequest:Proxy"] = "http://pr.proxynet.io:8000"
$PSDefaultParameterValues["Invoke-WebRequest:ProxyCredential"] = $cred
$PSDefaultParameterValues["Invoke-RestMethod:Proxy"] = "http://pr.proxynet.io:8000"
$PSDefaultParameterValues["Invoke-RestMethod:ProxyCredential"] = $cred

Invoke-RestMethod -Uri "https://api.ipify.org?format=json"   # pasa por el proxy

Las claves aceptan comodines, así que "Invoke-*:Proxy" cubre los dos cmdlets en una línea; las dos formas funcionaron en nuestra prueba. La tabla está vacía en cada sesión nueva a menos que las líneas estén en $PROFILE, y un script que se ejecuta con otra cuenta nunca ve tu perfil, así que deja los parámetros explícitos en todo lo que entregues.

Anular el proxy con -Proxy $null en una llamada directa falla con The cmdlet cannot run because the following parameter is missing: Proxy mientras exista un valor por defecto para ProxyCredential. Desactiva la tabla un momento en su lugar: $PSDefaultParameterValues["Disabled"] = $true, ejecuta la llamada y vuelve a ponerla en $false. En nuestra prueba, la llamada con Disabled salió directa y la siguiente volvió a pasar por el proxy.

¿PowerShell lee HTTP_PROXY y HTTPS_PROXY?

Windows PowerShell 5.1 no. Con $env:HTTP_PROXY y $env:HTTPS_PROXY definidas y sin parámetro -Proxy, nuestra petición fue directa al destino; el proxy nunca la vio. curl.exe, git y pip en el mismo equipo sí las siguen, de ahí la confusión.

PowerShell 7 sí, según su documentación: desde 7.0 los cmdlets toman su proxy por defecto de HttpClient.DefaultProxy de .NET, que en Windows lee primero HTTP_PROXY, HTTPS_PROXY, ALL_PROXY y la lista de exclusión NO_PROXY, y recurre a la configuración de proxy del usuario cuando las variables están vacías.

powershell
# Solo PowerShell 7; 5.1 ignora estas variables
$env:HTTPS_PROXY = "http://pr.proxynet.io:8000"
$env:NO_PROXY = "localhost,127.0.0.1,.internal.example"
Invoke-WebRequest -Uri "https://httpbin.org/ip"            # por el proxy
Invoke-WebRequest -Uri "https://httpbin.org/ip" -NoProxy   # directo, solo esta llamada

Las credenciales siguen yendo en -ProxyCredential, por llamada o mediante $PSDefaultParameterValues, exactamente igual que en 5.1.

¿Qué pasa con el proxy del sistema de Windows y netsh winhttp?

Sin -Proxy, 5.1 recurre al proxy de las Opciones de Internet del usuario actual, el ajuste que Windows muestra en Configuración, Red e Internet, Proxy. La documentación de .NET Framework indica que WebRequest.DefaultWebProxy lee primero el archivo de configuración de la aplicación y, si no existe, el proxy de las Opciones de Internet del usuario. Un script que de repente sale por un proxy inesperado suele estar siguiendo ese ajuste; Configuración de proxy en Windows y Chrome muestra dónde está.

Windows mantiene un segundo almacén de proxy para WinHTTP, la biblioteca HTTP que usan los servicios y los componentes del sistema. Se gestiona con netsh winhttp y no afecta a los cmdlets de PowerShell, que usan .NET. Los comandos siguientes vienen de la referencia de netsh de Microsoft y no se ejecutaron aquí, porque cambian un ajuste de todo el equipo:

text
netsh winhttp show proxy
netsh winhttp set proxy proxy-server="pr.proxynet.io:8000" bypass-list="*.internal.example"
netsh winhttp import proxy source=ie
netsh winhttp reset proxy

set proxy acepta un servidor y una lista de exclusión separada por puntos y coma, y nada más, así que un proxy que pide usuario y contraseña se configura mejor por script con -ProxyCredential, o se deja sin contraseña con una lista de IP autorizadas.

¿Cómo se usa el proxy desde clases .NET en un script?

Los scripts que llaman directamente a System.Net.WebClient o HttpWebRequest no ven -Proxy; leen el WebRequest.DefaultWebProxy de todo el proceso. Definirlo una vez enruta por el proxy esas clases y, en 5.1, cada Invoke-WebRequest sin su propio -Proxy:

powershell
$proxy = New-Object System.Net.WebProxy("http://pr.proxynet.io:8000")
$proxy.Credentials = New-Object System.Net.NetworkCredential("user", "pass")
$proxy.BypassList = @("^http://intranet\.example\.")
[System.Net.WebRequest]::DefaultWebProxy = $proxy

(New-Object System.Net.WebClient).DownloadString("https://api.ipify.org")

El ajuste vive solo en ese proceso de PowerShell. BypassList contiene expresiones regulares que se comparan con la URL completa; en nuestra prueba, el patrón de la intranet se excluyó y el host público no.

Una regla de WebProxy nos costó una hora: las peticiones a localhost o 127.0.0.1 nunca usan proxy. IsBypassed devolvió True para las dos incluso con BypassProxyOnLocal en False, así que todas las pruebas contra un servidor web local salieron directas y parecía que el proxy funcionaba. Prueba contra una dirección pública o dale al servidor local un nombre de host con un punto.

¿Invoke-WebRequest admite SOCKS5?

En Windows PowerShell 5.1, no. -Proxy "socks5://pr.proxynet.io:1080" se detiene con The ServicePointManager does not support proxies with the socks5 scheme., y una dirección de proxy https:// falla igual, así que 5.1 solo habla con los proxies por HTTP sin cifrar.

PowerShell 7.2 y posteriores se ejecutan sobre .NET 6 o más reciente, y el anuncio de red de .NET 6 de Microsoft documenta la compatibilidad con SOCKS4, SOCKS4a y SOCKS5 en HttpClient mediante una dirección de proxy socks5://. La referencia del cmdlet no menciona SOCKS y no pudimos ejecutar 7 aquí, así que trata -Proxy "socks5://pr.proxynet.io:1080" como algo que debes verificar en tu versión.

En 5.1, la vía práctica es el cURL real que viene con Windows 10 y 11: curl.exe -x "socks5h://user:pass@pr.proxynet.io:1080" https://httpbin.org/ip, con socks5h para que el proxy resuelva el nombre de host. Las opciones se explican en cURL con proxy; pon en lugar de 1080 el puerto SOCKS5 que muestra tu Generador de endpoints.

Ejemplo: descargar una lista de URL a través de un proxy con reintentos

El script descarga una lista de URL a través del proxy, reintenta los fallos temporales con una pausa creciente, se detiene de inmediato ante un 407 e imprime una línea por URL. Solo necesita Windows PowerShell 5.1:

powershell
# fetch.ps1: cada URL de la lista por el proxy, con reintentos
$ErrorActionPreference = "Stop"
$ProxyUri = "http://pr.proxynet.io:8000"
$ProxyCred = New-Object System.Management.Automation.PSCredential (
    "user", (ConvertTo-SecureString "pass" -AsPlainText -Force))

$urls = @(
    "https://httpbin.org/ip",
    "https://httpbin.org/status/503",
    "https://httpbin.org/status/404",
    "https://httpbin.org/headers"
)
$retryCodes = 408, 429, 500, 502, 503, 504

function Get-WithRetry {
    param([string]$Url, [int]$Attempts = 4)
    for ($i = 1; $i -le $Attempts; $i++) {
        $sw = [System.Diagnostics.Stopwatch]::StartNew()
        try {
            $r = Invoke-WebRequest -UseBasicParsing -Uri $Url -Proxy $ProxyUri -ProxyCredential $ProxyCred -TimeoutSec 30
            return [pscustomobject]@{ Url = $Url; Status = $r.StatusCode; Attempts = $i; Ms = $sw.ElapsedMilliseconds; Bytes = $r.RawContentLength }
        } catch [System.Net.WebException] {
            $code = 0
            if ($_.Exception.Response) { $code = [int]$_.Exception.Response.StatusCode }
            if ($code -eq 407) { throw "Proxy refused the credentials (407). Check user:pass or the IP whitelist." }
            if (($code -in $retryCodes -or $code -eq 0) -and $i -lt $Attempts) {
                $wait = [math]::Pow(2, $i - 1)   # 1, 2, 4 segundos
                Write-Warning "$Url -> $code ($($_.Exception.Status)); retry $i in $wait s"
                Start-Sleep -Seconds $wait
                continue
            }
            return [pscustomobject]@{ Url = $Url; Status = $code; Attempts = $i; Ms = $sw.ElapsedMilliseconds; Bytes = 0 }
        }
    }
}

$results = foreach ($u in $urls) { Get-WithRetry -Url $u }
$results | Format-Table -AutoSize

En nuestra ejecución, la dirección /status/503 respondió 503 dos veces, se reintentó tras 1 y 2 segundos y tuvo éxito al tercer intento porque el servidor de prueba se recupera; el 404 volvió sin reintento:

text
WARNING: https://httpbin.org/status/503 -> 503 (ProtocolError); retry 1 in 1 s
WARNING: https://httpbin.org/status/503 -> 503 (ProtocolError); retry 2 in 2 s

Url                            Status Attempts Ms Bytes
---                            ------ -------- -- -----
https://httpbin.org/ip            200        1 34    55
https://httpbin.org/status/503    200        3 11    65
https://httpbin.org/status/404    404        1  3     0
https://httpbin.org/headers       200        1 10   250

Tres decisiones importan. $_.Exception.Response.StatusCode es el único lugar donde 5.1 expone el estado de una petición fallida, porque un 4xx o un 5xx se lanza como error en lugar de devolverse. Un $code de 0 significa que no hubo respuesta, por ejemplo un tiempo de espera agotado, y también se reintenta. -TimeoutSec 30 limita cada intento; el valor por defecto, 0, espera indefinidamente. PowerShell 7 tiene integrado -MaximumRetryCount 3 -RetryIntervalSec 2, y su documentación dice que un 429 con Retry-After anula ese intervalo.

Cada Invoke-WebRequest abre su propia conexión al proxy, así que un gateway rotativo da a cada llamada una IP de salida distinta. Un inicio de sesión seguido de varias páginas necesita lo contrario: una sesión fija en el nombre de usuario mantiene la misma IP entre 1 y 60 minutos en los Proxies residenciales, mientras -SessionVariable conserva las cookies de tu lado.

¿Dónde aparecen los scripts de PowerShell con proxy?

Las tareas programadas son su lugar habitual: una tarea nocturna comprueba el precio y el stock de tus propios anuncios tal como los ve un comprador de otro país, un script de seguimiento pide tu sitio desde varias regiones y avisa si la moneda o el idioma no son los correctos, un agente de compilación detrás de un proxy corporativo instala módulos a través de él. A una API de un socio que solo autoriza una dirección se llega desde una tarea cuya salida nunca cambia; los Proxies ISP dan a esa tarea una IP dedicada que sobrevive a los reinicios.

Errores comunes de proxy en PowerShell y cómo solucionarlos

Los mensajes aparecen tal como los imprime Windows PowerShell 5.1 con la interfaz en inglés; en un Windows en español o en alemán, la parte .NET del texto está traducida.

  • The remote server returned an error: (407) Proxy Authentication Required. Falta -ProxyCredential, las credenciales están dentro de la dirección, la contraseña es incorrecta o la IP no está en la lista de IP autorizadas. $_.Exception.Response.StatusCode.value__ da 407 en un bloque catch; las causas están reunidas en Cómo solucionar HTTP 407 Proxy Authentication Required.
  • Unable to connect to the remote server con Status : ConnectFailure. Puerto del proxy incorrecto, un firewall en medio o el proxy está caído.
  • The proxy name could not be resolved: 'pr.proxynet.io'. Una errata en el nombre de host o no hay DNS; el mensaje nombra al proxy, no al destino.
  • The operation has timed out. -TimeoutSec es más corto de lo que necesitan el proxy y el destino; nuestro límite de 2 segundos contra una página de 5 segundos lo produjo.
  • The ServicePointManager does not support proxies with the socks5 scheme. Una dirección de proxy socks5:// o https:// en 5.1; usa http://, PowerShell 7.2+ o curl.exe.
  • A parameter cannot be found that matches parameter name 'x'. Un comando de cURL escrito en 5.1, donde curl es un alias de Invoke-WebRequest; escribe curl.exe.
  • Object reference not set to an instance of an object. Una llamada de 5.1 sin -UseBasicParsing en un script.
  • The cmdlet cannot run because the following parameter is missing: Proxy. -Proxy $null mientras $PSDefaultParameterValues todavía contiene un ProxyCredential; usa la clave Disabled en su lugar.
  • El proxy se omite en silencio. El destino es localhost o 127.0.0.1, o el script confía en HTTPS_PROXY en 5.1. Compara api.ipify.org con y sin -Proxy.

Guía de decisión

NecesidadRecomendación
Un comando a través del proxy-Proxy más -ProxyCredential
Todos los comandos de una sesión$PSDefaultParameterValues en $PROFILE
Sin contraseña en el scriptLista de IP autorizadas en el panel, solo -Proxy
Proxy corporativo con inicio de sesión de Windows-ProxyUseDefaultCredentials
Una llamada sin el proxy-NoProxy (7) o la clave Disabled (5.1)
SOCKS5PowerShell 7.2+, o curl.exe con socks5h://
Código con WebClient o HttpWebRequest[System.Net.WebRequest]::DefaultWebProxy
La misma IP de salida en un flujo de inicio de sesiónSesión fija en el nombre de usuario
Servicios y Windows Updatenetsh winhttp set proxy

Preguntas frecuentes

¿Cómo configuro un proxy para Invoke-WebRequest?

Añade -Proxy "http://host:port" y, si el proxy pide contraseña, -ProxyCredential con un objeto PSCredential. Los mismos dos parámetros existen en Invoke-RestMethod y, según la documentación de PowerShellGet 2.x, en Install-Module.

¿Por qué Invoke-WebRequest devuelve 407 Proxy Authentication Required?

Porque el proxy no recibió credenciales utilizables. En 5.1 ocurre cuando el usuario y la contraseña se escriben en la dirección en lugar de en -ProxyCredential, cuando la contraseña es incorrecta o cuando el proxy espera tu IP en una lista de IP autorizadas y la dirección del equipo cambió.

¿Invoke-WebRequest usa la configuración de proxy de Windows?

Sí, cuando no indicas -Proxy. Windows PowerShell 5.1 toma el proxy de las Opciones de Internet del usuario actual; PowerShell 7 lee primero HTTP_PROXY y HTTPS_PROXY y recurre a los ajustes del usuario. El proxy de WinHTTP definido con netsh winhttp es un almacén aparte y no lo lee ninguna de las dos.

¿Cómo evito el proxy en una sola petición en PowerShell?

En PowerShell 7, añade -NoProxy a esa llamada. En 5.1, omite -Proxy; si $PSDefaultParameterValues lo añade de todos modos, pon $PSDefaultParameterValues["Disabled"] = $true alrededor de la llamada. Para hosts enteros, usa NO_PROXY en 7 o una BypassList en un objeto WebProxy en 5.1.

¿Puedo usar un proxy SOCKS5 con Invoke-WebRequest?

En Windows PowerShell 5.1 no, porque rechaza el esquema socks5://. PowerShell 7.2 y posteriores se ejecutan sobre .NET 6+, que admite proxies SOCKS5 en HttpClient; verifícalo en tu versión. En 5.1, llama a curl.exe con socks5h:// en su lugar.

¿Cómo compruebo que PowerShell está usando realmente el proxy?

Pide https://api.ipify.org una vez sin -Proxy y otra con él: dos direcciones distintas, la segunda en el país del proxy, significan que el proxy está en uso. La misma dirección dos veces significa que la llamada salió directa, casi siempre porque el destino era localhost o porque 5.1 ignoró las variables que definiste.

En resumen

Pasa el proxy a Invoke-WebRequest e Invoke-RestMethod con -Proxy y un PSCredential en -ProxyCredential, nunca dentro de la dirección; fija los dos una vez en $PSDefaultParameterValues cuando todas las llamadas deban usarlo. Windows PowerShell 5.1 ignora HTTP_PROXY, recurre a las Opciones de Internet, excluye las direcciones de loopback y no puede hablar SOCKS5; PowerShell 7 lee las variables, añade -NoProxy y reintentos, y acepta SOCKS5 desde 7.2. Mantén -UseBasicParsing y -TimeoutSec en cada script de 5.1 y prueba contra una dirección pública. Los endpoints que aceptan exactamente estos parámetros están en la página de proxies de Proxynet.