Hardening a v3 Onion Service Webserver on a VPS
A Tor onion service hides your server's IP at the network layer, but the webserver behind it still leaks identity at the application layer. A version banner, a Date header that exposes your timezone, image EXIF with GPS coordinates, or an Onion-Location header that ties your hidden service to a clearnet domain can all undo the anonymity Tor gives you. This guide walks through hardening an nginx v3 onion service on a VPS so the only thing reachable is the onion address, and the response itself reveals nothing.
Need this done for your project?
We implement, you ship. Async, documented, done in days.
Bind the webserver to localhost only
The most common and most damaging mistake on an onion-service VPS is leaving the webserver listening on a public interface. Tor delivers requests to your service over the loopback interface: in your torrc you set something like HiddenServicePort 80 127.0.0.1:8080, and Tor forwards onion traffic to 127.0.0.1:8080. If nginx also binds 0.0.0.0:8080 (or :80), the exact same content is reachable directly over the clearnet by anyone who scans your VPS IP. Scanners like Shodan and Censys index those pages, and a single matching string, favicon hash, or page title links your onion address to your real IP. The hidden service is then pointless.
Bind nginx to loopback only. In the server block, the listen directive must name the loopback address explicitly:
server {
listen 127.0.0.1:8080;
server_name _;
# ... onion content ...
}Never use a bare listen 8080; on an onion vhost, because that binds all interfaces. After reloading nginx, verify with ss -tlnp that the socket shows 127.0.0.1:8080 and not 0.0.0.0:8080 or *:8080. Back this up at the firewall layer with a default-deny inbound policy that only permits the SSH port you actually use; nothing else should accept connections from the public interface. On a clean offshore VPS with full root access you control both layers, so there is no shared edge proxy quietly re-exposing your service.
If the box serves both a clearnet site and a separate onion service, give each its own server block on its own loopback port, and never let the onion vhost answer the default server_name on a public listener. Test from outside the VPS with curl http://YOUR_VPS_IP:8080/; it must time out or be refused, not return your page.
Strip the Server, Date, and other fingerprintable headers
Even when only Tor can reach your service, the HTTP response itself is a fingerprint. Default responses advertise software versions, build details, and a clock. Each one narrows down who you are and makes correlation across services easier.
- Server header. nginx sends
Server: nginx/1.24.0by default. Setserver_tokens off;in the http block to drop the version, leaving justServer: nginx. To remove the token entirely you need theheaders-moremodule (more_clear_headers Server;ormore_set_headers "Server: ";) or a build with the version string patched out. The version number tells an adversary exactly which CVEs apply to you. - Date header. The
Dateresponse header reports your server's clock. If it drifts from UTC or matches a particular timezone offset, it becomes a correlation signal across your services. Keep the VPS clock synced to UTC with NTP, and consider clearing the header at the proxy where your application allows it. Many onion operators deliberately serve UTC and nothing else. - X-Powered-By and framework banners. PHP emits
X-Powered-By: PHP/8.x; setexpose_php = Offin php.ini. Application frameworks (Express, Flask behind a header, etc.) often add their own banners; clear them at nginx withheaders-moreor in the app config. - ETag and Last-Modified. These leak file inode numbers and exact modification timestamps, which can fingerprint your filesystem and deploy times. Disable with
etag off;and, where you do not need conditional requests, suppressLast-Modifiedfor static assets.
A practical baseline in the http block:
server_tokens off;
etag off;
# with headers-more-nginx-module:
more_clear_headers Server;
more_clear_headers Date;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;After deploying, inspect what you actually send over Tor with torsocks curl -sI http://youraddress.onion/ and read every line. The goal is a response that names no software, no version, and no timezone-revealing clock you did not intend to expose.
Remove EXIF and document metadata before publishing
The webserver is not the only thing that talks. Files you publish carry metadata that can be far more revealing than any header. A JPEG straight from a phone or camera can contain GPS coordinates of where it was taken, the device make and model, the serial number on some cameras, and timestamps. A PDF or Office document embeds the author name, the software and OS that produced it, and revision history. Publishing one such file on an onion service can deanonymize the operator completely, independent of every network-layer protection Tor provides.
Strip metadata from everything before it touches the document root. For images, exiftool removes all tags:
exiftool -all= -overwrite_original image.jpg
# batch a directory:
exiftool -all= -overwrite_original -r ./uploads/For a stronger guarantee, the mat2 tool re-renders files to drop metadata that simple tag deletion can miss, and it understands PDFs, Office documents, and many image formats. Verify the result with exiftool image.jpg and confirm only generic fields remain.
If your onion service accepts uploads from visitors, this cannot be a manual step. Run every uploaded file through a metadata-stripping pipeline on the server before it is stored or served, and reject formats you cannot sanitize. Also normalize filenames, because user-supplied names can themselves carry identifying text. Treat the document root as a publication surface: nothing reaches it without passing through the stripper, the same way nothing reaches the network without passing through Tor.
Avoid Onion-Location and clearnet cross-links
The Onion-Location header (and its <meta http-equiv="onion-location"> equivalent) is designed for the opposite use case from yours: it lets a public clearnet site advertise its onion mirror to Tor Browser. If you run a standalone onion service, you almost certainly do not want it, and serving it by accident does real harm: it publicly links your onion address to a clearnet domain, which is exactly the correlation an onion service exists to prevent. Check that neither nginx nor your application emits this header, and remove any leftover add_header Onion-Location directive copied from a tutorial.
The same caution applies to every outbound reference in your pages. Common leaks to audit:
- Absolute clearnet URLs. Any link, image
src, stylesheet, or script that points athttps://yourdomain.com/...instead of a relative path causes Tor Browser to make a clearnet request, tying the visitor's session and your content to that domain. Use relative URLs throughout, or reference the onion address explicitly. - Third-party assets. Fonts from a CDN, analytics scripts, embedded maps, or remote favicons all generate off-onion requests. Self-host every asset. An onion service that loads a Google Font is no longer self-contained.
- Canonical and hreflang tags. A
<link rel="canonical">pointing at a clearnet URL hands the correlation to anyone who reads the source. Either omit it or point it at the onion address. - Redirects and error pages. A misconfigured redirect to a clearnet host, or a default error page that references your real hostname, leaks just as effectively as a link in the body.
Audit the rendered HTML, not just the templates: torsocks curl -s http://youraddress.onion/ | grep -Eo 'https?://[^\"'\'' ]+' lists every absolute URL your homepage emits. Repeat for key pages. The target state is an onion service that, when fully loaded in Tor Browser, makes zero requests outside the onion network.
Lock the rest of the surface and choose the right host
Header and metadata hygiene only matters if the rest of the box is quiet too. A few operational rules close the remaining gaps:
- Use client authorization for non-public services. Tor v3 onion services support client authentication: only clients holding a configured key can resolve and reach the service. For an admin panel or a private site this removes it from the public onion space entirely, so it cannot be crawled or probed even if the address leaks.
- Disable directory listings and remove default files. Set
autoindex off;, delete the stock nginx welcome page, and make sure no/server-status, phpinfo page, or backup file sits in the document root. These are the first things a scanner looks for. - Keep management off the onion vhost. SSH on a non-standard port with key-only auth, fail2ban, and automatic security updates. The version number you stripped from the Server header is the one an attacker needs to pick an exploit, so patch promptly regardless.
- Separate identities per service. If you run several onion services, give each its own nginx config, its own document root, and ideally its own user, so a shared header, error page, or favicon does not correlate them.
- Consider Vanguards. For services that need protection against guard-discovery and traffic-correlation attacks, the Vanguards add-on hardens Tor's path selection. It is worthwhile for high-risk operators.
The host underneath all of this matters because the failure modes above are mostly about what the machine reveals when probed. A clean VPS IP with no shared edge proxy, full root control over nginx and the firewall, and a provider that does not require identifying registration details gives you a surface you can actually keep silent. For a single hardened onion service an offshore VPS with root access is the right fit; for higher traffic, many services, or heavier upload pipelines, an offshore dedicated server gives the headroom without a noisy shared environment. The discipline is the same at any size: only the onion address is reachable, and the response behind it names nothing.
Serviços Relacionados
Privacy & anti-censorship guides
Why Anubiz Host
Ready to get started?
Skip the research. Tell us what you need, and we'll scope it, implement it, and hand it back — fully documented and production-ready.