Guides

wget and curl on Windows: curl.exe, PowerShell aliases and GNU wget

wget for Windows: use the curl.exe built into Windows 10, 11 and Server 2019+, PowerShell's Invoke-WebRequest, or GNU wget from winget or Chocolatey.

Published ~9 min read
On this page

Short answer: Windows has no wget, but Windows 10 (1803 and later), Windows 11 and Windows Server 2019 and later include curl.exe. Download a file with curl.exe -L -o file.zip URL. In Windows PowerShell 5.1, curl and wget are aliases for Invoke-WebRequest, so type curl.exe. For GNU wget itself, use winget install JernejSimoncic.Wget or choco install wget.

PowerShell
curl.exe -L -o file.zip https://example.com/file.zip      # built in, works everywhere
Invoke-WebRequest https://example.com/file.zip -OutFile file.zip -UseBasicParsing   # PowerShell only
winget install JernejSimoncic.Wget                         # real GNU wget, then open a new window

The rest covers what each Windows version ships, why curl and wget behave so strangely in PowerShell, the everyday download commands for curl.exe and Invoke-WebRequest, the TLS 1.2 fix for older PowerShell, GNU wget for the jobs curl cannot do, and the errors that show up on Windows only.

What Windows includes

Microsoft added curl.exe and tar.exe to Windows 10 with Insider build 17063 in December 2017, announced on its Command Line blog. Every release since then has them in C:\Windows\System32, which includes Windows Server 2019 (build 17763) and everything after it.

Windows versioncurl.exewgetPowerShell
Windows 10 1803 or later, Windows 11YesNo5.1, with curl and wget aliases
Windows Server 2019, 2022, 2025YesNo5.1, with curl and wget aliases
Windows Server 2016 and older, Windows 10 before 1803NoNo5.1 (or older), aliases included
PowerShell 7, installed separatelyUses the system curl.exeNoNo curl or wget alias

Windows’ own curl.exe is built by Microsoft, uses the Windows TLS stack (Schannel) and the Windows certificate store, and changes only when Windows updates it. The curl project’s page about it notes that Microsoft’s version lags behind curl’s own releases and asks that problems with it go to Microsoft. curl.exe -V shows which one you have.

Why curl and wget act strangely in PowerShell

Windows PowerShell 5.1, the version every current Windows and Windows Server ships, defines curl and wget as aliases for its own Invoke-WebRequest cmdlet. Aliases win over programs on the PATH, so typing curl never reaches curl.exe:

Windows PowerShell 5.1
PS> Get-Command curl

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           curl -> Invoke-WebRequest
  • curl options fail. curl -L https://example.com ends with A parameter cannot be found that matches parameter name 'L'., because the cmdlet has no -L. -o fails too: PowerShell rejects it as ambiguous, since it could mean -OutFile, -OutVariable or -OutBuffer.
  • wget saves nothing. wget https://example.com/file.zip prints a response object (StatusCode, Content, RawContent) to the console instead of writing a file, because Invoke-WebRequest only saves with -OutFile.
  • A security prompt appears. Since Microsoft’s security update of 9 December 2025 (CVE-2025-54100), Invoke-WebRequest in PowerShell 5.1 asks for confirmation with Security Warning: Script Execution Risk unless you pass -UseBasicParsing. The aliases never pass it, so a script with a bare curl URL or wget URL now stops and waits.

The fixes, from simplest:

  • Type curl.exe (with the extension). That bypasses the alias in every shell and also works in cmd and in PowerShell 7, so scripts stay portable.
  • Remove the aliases in your profile (notepad $PROFILE): Remove-Item Alias:curl, Alias:wget -Force -ErrorAction SilentlyContinue.
  • Use PowerShell 7, which dropped both aliases; there curl runs curl.exe.

In cmd there are no aliases: curl is curl.exe, and wget is 'wget' is not recognized as an internal or external command until you install it.

Download files with curl.exe

PowerShell
cd $env:USERPROFILE\Downloads                               # PowerShell; in cmd: cd %USERPROFILE%\Downloads
curl.exe -L -O https://example.com/files/tool.zip            # keep the remote file name
curl.exe -L -o tool.zip "https://example.com/get?id=42"      # choose the name; quote URLs with ? or &
curl.exe -L -C - -o big.iso https://example.com/big.iso      # resume a broken download
curl.exe -L --retry 5 --retry-delay 3 -f -o tool.zip https://example.com/tool.zip
curl.exe -I https://example.com                              # headers only
curl.exe -s -o NUL -w "%{http_code}" https://example.com     # just the status code
tar.exe -xf tool.zip                                         # Windows' tar also unpacks .zip

-L follows redirects, which most download links use; without it you save a tiny redirect page instead of the file. -f makes curl fail on a 404 or 500 instead of saving the error page under your file name. The first line matters on a server: an elevated PowerShell starts in C:\Windows\System32, and -O would drop the file there.

The headers-only request returns the same thing on Windows as on Linux:

Output · curl -sI https://example.com, run 21 Oct 2026 from Debian 12 (first lines)
HTTP/2 200
date: Wed, 21 Oct 2026 20:15:42 GMT
content-type: text/html; charset=utf-8
server: cloudflare
last-modified: Tue, 20 Oct 2026 23:45:21 GMT

Windows-specific details

  • NUL, not /dev/null. Windows’ null device is NUL in cmd and PowerShell alike.
  • Percent signs in batch files. In a .bat or .cmd file, cmd eats single percent signs, so write -w "%%{http_code}". At the interactive prompt one % is fine.
  • JSON bodies. Windows PowerShell 5.1 strips the inner double quotes from arguments it passes to programs, so -d with inline JSON arrives broken. Save the JSON in a file and send it with -d "@body.json", which works in every shell, or use Invoke-RestMethod below.

The curl command examples guide covers POST data, headers, authentication and -w in depth. The flags work the same with curl.exe, as long as your build has the feature: curl.exe -V lists its protocols and features.

wget commands and their curl.exe equivalents

TaskGNU wgetcurl.exe
Download, keep the namewget URLcurl.exe -L -O URL
Download under a new namewget -O out.zip URLcurl.exe -L -o out.zip URL
Into a folderwget -P C:\dl URLcurl.exe -L -o C:\dl\file.zip URL
Resumewget -c URLcurl.exe -L -C - -O URL
Limit the speedwget --limit-rate=2m URLcurl.exe -L --limit-rate 2M -O URL
Quietwget -q URLcurl.exe -s -L -O URL
Many URLs from a filewget -i urls.txtA loop, or url = lines in a -K config file
Mirror a directory recursivelywget -r -np -nH URLNot possible: curl does not crawl

Invoke-WebRequest and Invoke-RestMethod

Inside PowerShell scripts the native cmdlets are often the better choice, because they return objects instead of text:

Windows PowerShell 5.1 or PowerShell 7
$ProgressPreference = 'SilentlyContinue'     # the progress bar slows 5.1 down badly
Invoke-WebRequest -Uri https://example.com/tool.zip -OutFile "$env:TEMP\tool.zip" -UseBasicParsing

# status code and headers without saving
$r = Invoke-WebRequest -Uri https://example.com -Method Head -UseBasicParsing
$r.StatusCode
$r.Headers['Content-Type']

-UseBasicParsing matters only in 5.1: it skips the Internet Explorer HTML engine, which Server Core does not have and which a server where IE was never opened refuses to use, and it avoids the security prompt. PowerShell 7 always parses this way and ignores the switch. For a non-success status, Invoke-WebRequest throws an error instead of returning, so wrap checks in try/catch.

For JSON APIs, Invoke-RestMethod converts the answer into PowerShell objects and builds the request body for you:

PowerShell
$body = @{ name = 'backup-01'; size = 40 } | ConvertTo-Json
$res  = Invoke-RestMethod -Uri https://api.example.com/v1/items -Method Post -ContentType 'application/json' -Body $body -Headers @{ Authorization = "Bearer $token" }
$res.id

For large files on a slow or unstable line, Start-BitsTransfer -Source URL -Destination C:\dl\big.iso uses the Background Intelligent Transfer Service, which is built to pick a transfer up again after a network interruption.

“Could not create SSL/TLS secure channel” in older PowerShell

Windows PowerShell runs on .NET Framework, and unless the registry says otherwise, .NET can leave its web cmdlets with old protocol versions that websites refuse today. The result is The request was aborted: Could not create SSL/TLS secure channel. For the current session:

Windows PowerShell 5.1
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12

For good, Microsoft’s “TLS best practices with .NET Framework” page documents two registry values that let .NET use strong cryptography and the operating system’s TLS defaults. Set them in both the 64-bit and the 32-bit branch, then open a new PowerShell window:

PowerShell (Administrator)
foreach ($key in 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319',
                 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319') {
  Set-ItemProperty -Path $key -Name SchUseStrongCrypto -Value 1 -Type DWord
  Set-ItemProperty -Path $key -Name SystemDefaultTlsVersions -Value 1 -Type DWord
}

curl.exe does not depend on these settings: it talks to Schannel directly and uses the operating system’s protocol defaults, not .NET’s. On Server 2019 and later it is the quickest way around the problem.

Install GNU wget on Windows

You need real wget when a script or tutorial expects its options, or for what curl cannot do at all: recursive downloads and mirroring a directory listing.

PowerShell
winget install JernejSimoncic.Wget        # Windows 10/11, Server 2025
choco install wget -y                     # Chocolatey, any Windows Server

Both packages use the Windows builds Jernej Simončič publishes, but they do not carry the same version: on 21 October 2026 the newest wget in Microsoft’s winget repository was 1.21.4, while the Chocolatey package was at 1.25.0. The winget package is a portable one: winget puts wget.exe in its own folder and a link to it in %LOCALAPPDATA%\Microsoft\WinGet\Links (machine scope: C:\Program Files\WinGet\Links), which is on the PATH for windows opened afterwards. Without a package manager, download wget.exe from the same builds and put it in a folder that is on your PATH.

PowerShell · GNU wget
wget.exe -c https://example.com/big.iso                      # resume if interrupted
wget.exe -i urls.txt -P C:\dl                                 # every URL in a file, into C:\dl
wget.exe -r -np -nH --cut-dirs=1 https://example.com/files/  # mirror one directory, no parents
wget.exe --limit-rate=2m https://example.com/big.iso

If wget stops with Unable to locally verify the issuer's authority, that build has not found a CA certificate bundle. Point it at one with --ca-certificate=C:\path\cacert.pem rather than reaching for --no-check-certificate, which turns verification off entirely. Inside WSL you are on Linux, and the Debian or Ubuntu steps in wget: command not found apply.

A newer curl than the one Windows ships

PowerShell or cmd
winget install cURL.cURL
where.exe curl

The cURL.cURL package installs the curl project’s own Windows build from curl.se, which follows curl’s releases closely. The catch is the search order: the system PATH comes before the user PATH, so C:\Windows\System32\curl.exe still answers to curl.exe. where.exe curl lists every copy in the order Windows tries them; call the new one by the full path it prints, or point an alias in your PowerShell profile at it. Do not replace the file in System32: Windows owns it, and an update or a system file check puts Microsoft’s version back. If curl is missing altogether on an older Windows, curl: command not found covers that case for every system.

Errors that happen only on Windows

MessageCauseFix
A parameter cannot be found that matches parameter name 'L'.PowerShell 5.1 alias: curl is Invoke-WebRequestType curl.exe
Security Warning: Script Execution RiskInvoke-WebRequest without -UseBasicParsing after the December 2025 updatecurl.exe, or add -UseBasicParsing
'wget' is not recognized as an internal or external commandWindows has no wgetInstall it, or use curl.exe
Could not create SSL/TLS secure channel.NET in Windows PowerShell not using TLS 1.2Session fix or registry values above
curl: (35) schannel: ... (0x80092012) ... unable to check revocationSchannel could not reach the certificate's revocation list, often behind a proxy or firewall--ssl-revoke-best-effort
Unable to locally verify the issuer's authoritywget build without a CA bundle--ca-certificate=cacert.pem

For the revocation error, --ssl-revoke-best-effort (a Schannel-only option) ignores a revocation check only when the list cannot be reached, which is safer than --ssl-no-revoke, which switches the check off completely. The curl manual describes both; if every site fails this way, the server’s outbound traffic to the certificate authorities’ revocation servers is what to fix.

Downloads on a fresh Windows server

On a new Windows server, curl.exe is the tool to reach for first: it is already there, it is not affected by the .NET TLS defaults, and one line fetches an installer without opening a browser. Installing Chrome on Windows Server uses exactly that pattern, curl.exe or Invoke-WebRequest followed by a silent msiexec. On a Windows VPS with Windows Server 2022 or 2019 you log in over Remote Desktop as Administrator, so installing GNU wget or a newer curl next to the built-in one is up to you.

Frequently asked

Does Windows have wget?

No. Windows has no GNU wget, but Windows 10 (1803 and later), Windows 11 and Windows Server 2019 and later include curl.exe, which downloads a file with curl.exe -L -o file.zip URL. In Windows PowerShell 5.1, wget is only an alias for Invoke-WebRequest, which takes different options.

Why does curl not work in PowerShell?

In Windows PowerShell 5.1, curl is an alias for the Invoke-WebRequest cmdlet, so curl options fail: -L ends with 'A parameter cannot be found that matches parameter name', and -o is rejected as ambiguous. Type curl.exe to run the real curl, or use PowerShell 7, which removed the alias.

How do I install wget on Windows?

Run winget install JernejSimoncic.Wget on Windows 10, 11 or Server 2025, or choco install wget with Chocolatey on any Windows Server. Open a new terminal afterwards. In Windows PowerShell 5.1, type wget.exe, because the built-in wget alias still points to Invoke-WebRequest.

How do I download a file with PowerShell?

Invoke-WebRequest -Uri URL -OutFile file.zip -UseBasicParsing. In Windows PowerShell 5.1, set $ProgressPreference = 'SilentlyContinue' first, because the progress bar slows large downloads down, and keep -UseBasicParsing, which also avoids the security prompt Microsoft added in December 2025.

Related articles

All guides

curl command examples: GET, POST, headers, auth and redirects

curl -X POST -d for form data, curl -H for headers, curl -u for basic auth, curl -L for redirects, curl -o to save a file, with the actual output of each one.

wget: command not found? Install wget on Linux, macOS and Windows

wget is not installed. Fix: sudo apt install wget on Debian and Ubuntu, brew install wget on macOS, apk add wget on Alpine. Plus Windows, Docker and curl.

curl: command not found? How to fix it on any Linux distribution

curl is simply not installed. Fix: sudo apt install curl on Debian and Ubuntu, apk add curl on Alpine, dnf install curl on RHEL. Plus sudo, Docker and PATH.

Install winget on Windows Server 2025, 2022 and 2019, or use Chocolatey

Install winget on Windows Server 2025, 2022 and 2019 with Repair-WinGetPackageManager, use winget install and upgrade, or install Chocolatey instead.

How to install OpenSSL on Windows and Windows Server

Install OpenSSL on Windows with winget, Chocolatey or Git for Windows (openssl.org ships source only), fix PATH and openssl.cnf, and convert PFX to PEM.

curl ignore certificate: -k, --insecure and the safer fixes (wget, PowerShell)

curl -k (--insecure) makes curl ignore certificate errors, wget --no-check-certificate does the same. Why it is risky, and safer fixes: --cacert, pinning.

How to create a .bat file in Windows: Notepad, syntax and examples

How to create a .bat file: save it from Notepad as All files with a .bat name, then add @echo off, variables, arguments, if/for and three server scripts.