Updated October 2026: rewritten for the current Tor Expert Bundle, with a simpler setup (no firewall changes needed), a corrected multi-site config, an ASP.NET Core example, and backup scripts that work in both Windows PowerShell 5.1 and PowerShell 7.
This tutorial is for educational purposes. Make sure using Tor is legal where you live; you are responsible for how you use it.
Quick Answer: Where Is torrc on Windows?
- Tor Expert Bundle (tor.exe): by default Tor reads
%APPDATA%\tor\torrc, which isC:\Users\<you>\AppData\Roaming\tor\torrc. The file has no extension, and the folder does not exist until you create it. - Tor Browser:
<Tor Browser folder>\Browser\TorBrowser\Data\Tor\torrc. - Tor running as a Windows service: the service runs under a system account, so
%APPDATA%points to that account's profile, not yours. Avoid the guesswork: keep the file somewhere explicit such asC:\tor\torrcand start Tor with-f C:\tor\torrc, as shown below.
Key Points
- A Tor onion service (a ".onion link") lets people reach your site through Tor Browser without either side revealing its IP address.
- You can serve an existing IIS website over Tor on Windows Server for free with the official Tor Expert Bundle.
- You do not need to open any inbound firewall ports or own a domain name. Tor only makes outbound connections.
- The onion address is derived from a private key file. Back that file up, or you lose the address forever.
What You'll Need
- A Windows Server VPS (2019, 2022 or 2025) with Administrator access (use Softsys if you don't have a VPS)
- The Tor Expert Bundle for Windows (64-bit)
- A website running on IIS
Why Offer a .onion Version of Your Site?
Tor lets people browse without advertising networks, ISPs or the destination site learning their IP address. If you run a public clearnet site (for example a .com), offering an onion mirror reaches readers and customers who prefer Tor, and it keeps your site reachable for people on networks that block or monitor it.
Step 1: Bind Your IIS Site to Localhost
Tor will forward onion traffic to a local port, so the site only needs to listen on 127.0.0.1.
- Open IIS Manager, select your site (or create a new one with an
index.htmlcontaining something like testing123). - Click Bindings... > Add: type
http, IP address127.0.0.1, port8081, host name blank. - Check it from PowerShell on the server:
curl.exe -v http://127.0.0.1:8081
If you see your page, IIS is ready. An existing site can keep its normal public bindings; the extra localhost binding is what Tor will use.
No firewall rule is needed. Because the binding is on 127.0.0.1, only processes on the server (including tor.exe) can reach it. Tor connects out to the Tor network, so nothing new has to be opened inbound.
Step 2: Install the Tor Expert Bundle
Download the Windows Expert Bundle (a .tar.gz file) from the Tor Project, then extract it. Windows Server 2019 and later include tar.exe, so you no longer need 7-Zip:
# Run in an elevated PowerShell window
New-Item -ItemType Directory -Force -Path C:\tor, C:\tor\state, C:\tor\onion, C:\tor\logs | Out-Null
# Windows Server 2019+ includes tar.exe, so 7-Zip is optional
tar -xzf "$env:USERPROFILE\Downloads\tor-expert-bundle-windows-x86_64-*.tar.gz" -C C:\tor
# Confirm it runs
C:\tor\tor\tor.exe --version
# Create the (extensionless) config file and edit it
New-Item -ItemType File -Force -Path C:\tor\torrc | Out-Null
notepad C:\tor\torrc
After extracting you will have C:\tor\tor\tor.exe plus a C:\tor\data folder with GeoIP files.
Step 3: Write the torrc Configuration
Put this in C:\tor\torrc (no file extension):
# C:\tor\torrc
# Where Tor keeps its state (cached directory info, etc.)
DataDirectory C:\tor\state
# Server-only: no local SOCKS proxy needed. (Remove this line if you also
# want to use this Tor process as a client on port 9050.)
SocksPort 0
# Onion service #1 -> IIS site bound to 127.0.0.1:8081
HiddenServiceDir C:\tor\onion\mysite
HiddenServicePort 80 127.0.0.1:8081
# Onion service #2 (optional) -> another IIS site on 127.0.0.1:8082
#HiddenServiceDir C:\tor\onion\mysite2
#HiddenServicePort 80 127.0.0.1:8082
Log notice file C:\tor\logs\notices.log
- HiddenServiceDir is where Tor creates the keys and the
hostnamefile for that onion address. Use one directory per site. - HiddenServicePort 80 127.0.0.1:8081 means "visitors connect to port 80 on the onion address; forward them to IIS on 127.0.0.1:8081."
- Multiple sites: repeat only the
HiddenServiceDir+HiddenServicePortpair. One Tor process serves all of them; do not repeatSocksPort. - SocksPort 0 turns off the local proxy on 9050, which a server-only setup doesn't need.
Step 4: Run Tor as a Windows Service
Run Tor in the foreground first so you can watch it connect, then install it as a service so it starts automatically after a reboot:
# Test in the foreground first (Ctrl+C to stop). Look for "Bootstrapped 100% (done)".
C:\tor\tor\tor.exe -f C:\tor\torrc
# Then install it as a Windows service that starts automatically
C:\tor\tor\tor.exe --service install -options -f "C:\tor\torrc"
C:\tor\tor\tor.exe --service start
# Later, if you need to change the service's arguments:
C:\tor\tor\tor.exe --service stop
C:\tor\tor\tor.exe --service remove
In services.msc you will see Tor Win32 Service. Make sure its startup type is Automatic. If the service won't start, read C:\tor\logs\notices.log; the usual causes are a typo in the torrc or a service account that can't write to C:\tor\state or C:\tor\onion.
Step 5: Get Your .onion Address
Once Tor has started, open C:\tor\onion\mysite\hostname. It contains your address: 56 characters followed by .onion. The same folder holds hs_ed25519_secret_key and hs_ed25519_public_key.
On another computer, install Tor Browser and paste in the address. The first connection can take 30 seconds or more while the service publishes its descriptor. If you see your test page, you are running an onion service.
Running ASP.NET Core Over Tor (HTTPS Redirect Problem)
Most .NET apps need no changes, with one big exception: HTTPS redirects. Tor Browser connects to onion addresses over plain http:// (Tor already encrypts the connection end to end), so if your app redirects every HTTP request to HTTPS, onion visitors get bounced to a URL that doesn't work.
The clean fix is to skip the redirect when the request's host is your onion address. The same snippet also adds the Onion-Location header so Tor Browser users of your clearnet site are offered the onion version:
// Program.cs (ASP.NET Core): one app serving both https://example.com and the .onion address
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
const string OnionHost = "youronionaddressxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion";
static bool IsOnion(HttpContext ctx) =>
ctx.Request.Host.Host.EndsWith(".onion", StringComparison.OrdinalIgnoreCase);
// Tor Browser talks plain http to the onion address (Tor already encrypts end to end),
// so only force HTTPS for clearnet requests.
app.UseWhen(ctx => !IsOnion(ctx), clearnet =>
{
clearnet.UseHttpsRedirection();
clearnet.UseHsts();
});
// Advertise the onion mirror; Tor Browser shows ".onion available" (HTTPS responses only).
app.Use(async (ctx, next) =>
{
if (ctx.Request.IsHttps)
{
ctx.Response.Headers["Onion-Location"] =
$"http://{OnionHost}{ctx.Request.PathBase}{ctx.Request.Path}{ctx.Request.QueryString}";
}
await next();
});
app.MapGet("/", () => "Hello from clearnet and Tor");
app.Run();
If you redirect with the IIS URL Rewrite module instead, add a condition to that rule so it doesn't apply when {HTTP_HOST} ends in .onion.
Make the Onion Address Easy To Find
Tor Browser shows a purple ".onion available" button when an HTTPS page returns an Onion-Location header. On IIS you can set it in web.config without touching code:
<!-- web.config on the HTTPS (clearnet) site -->
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Onion-Location" value="http://youronionaddressxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.onion/" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
If you can't set headers, a meta tag works too: <meta http-equiv="onion-location" content="http://youraddress.onion/" />. It's also worth linking to the onion address in your footer.
Security Tips for Onion Sites
- Self-host assets. CSS, fonts and scripts loaded from CDNs make clearnet requests from the visitor's browser. Serve them from your own app instead.
- Work without JavaScript. Many Tor users use the "Safest" security level, which disables JavaScript. Make core pages work with HTML and CSS alone.
- Don't leak the server's IP. Error pages, response headers and email links can reveal it. Remove server headers and use generic error pages.
- Keep Tor updated. Old Tor releases stop being able to keep up with the network. Upgrade the Expert Bundle the same way you installed it (stop the service, replace the files, start it).
Back Up and Move Your Onion Service
An onion address isn't registered anywhere: it is the key in hs_ed25519_secret_key. If the server dies without a backup of that file, the address is gone. With a backup you can restore the same address on any server, Windows or Linux.
Back Up the Keys
# Back up an onion service's identity (PowerShell 5.1 and 7+).
# Whoever holds hs_ed25519_secret_key controls the .onion address. Store it offline.
$serviceDir = "C:\tor\onion\mysite"
$backupFile = "D:\secure-backup\mysite-onion-key.txt"
$hostname = (Get-Content -Path (Join-Path $serviceDir "hostname") -Raw).Trim()
$secretKey = [IO.File]::ReadAllBytes((Join-Path $serviceDir "hs_ed25519_secret_key"))
@(
$hostname
[Convert]::ToBase64String($secretKey)
) | Set-Content -Path $backupFile -Encoding ascii
Write-Output "Backed up $hostname to $backupFile"
Restore the Keys on a New Server
# Restore the onion service on a new server, BEFORE starting Tor there.
$serviceDir = "C:\tor\onion\mysite"
$backupFile = "D:\secure-backup\mysite-onion-key.txt"
$lines = Get-Content -Path $backupFile
New-Item -ItemType Directory -Force -Path $serviceDir | Out-Null
Set-Content -Path (Join-Path $serviceDir "hostname") -Value $lines[0] -Encoding ascii
[IO.File]::WriteAllBytes((Join-Path $serviceDir "hs_ed25519_secret_key"), [Convert]::FromBase64String($lines[1]))
# Tor recreates hs_ed25519_public_key from the secret key on start.
C:\tor\tor\tor.exe --service start
Get-Content (Join-Path $serviceDir "hostname") # should print the same .onion address
Frequently Asked Questions
Where is the torrc file on Windows?
For the Tor Expert Bundle the default is %APPDATA%\tor\torrc (C:\Users\<user>\AppData\Roaming\tor\torrc). For Tor Browser it is Browser\TorBrowser\Data\Tor\torrc inside the Tor Browser folder. When Tor runs as a service, pass the path explicitly with -f.
Do I need to open a port in Windows Firewall for a Tor onion service?
No. Tor only makes outbound connections, and IIS listens on 127.0.0.1, so no inbound rule is required.
Can I host multiple .onion sites on one Windows server?
Yes. Add another HiddenServiceDir and HiddenServicePort pair to the same torrc, each pointing at a different local port. One Tor service handles all of them.
Do I need an SSL certificate for a .onion site?
No. Connections to onion services are already end-to-end encrypted and authenticated by the onion address itself, so plain HTTP is normal. HTTPS certificates for onion addresses exist but are optional.
How do I keep the same .onion address on a new server?
Copy the hs_ed25519_secret_key file (and optionally hostname) into the new server's HiddenServiceDir before starting Tor. The address is derived from that key.
Summary
With the Tor Expert Bundle, a localhost binding in IIS, a five-line torrc and a Windows service, any IIS site can have its own .onion address. Exempt the onion host from HTTPS redirects, advertise it with Onion-Location, and keep an offline backup of the secret key.
Related: removing ASP.NET server headers and running a Tor onion service on Linux with nginx.

