SSD Nodes Learn Hosting plans →
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-27

Alojar un sitio .onion en un VPS con Tor y nginx

Configura un servicio onion v3 en Ubuntu con Tor y nginx en loopback. Evita fugas que relacionen la dirección .onion con la IP pública del VPS.

Qué va a crear

Un sitio onion es un servidor web normal que sólo responde a través de la red Tor. Instale tor, añada dos líneas a /etc/tor/torrc, lea la dirección que Tor escriba y, después, haga que nginx escuche en 127.0.0.1 para que nada responda en la IP pública. La instalación tarda diez minutos. El resto de esta guía es una lista de posibles fugas, porque un sitio onion suele fallar cuando su propia configuración apunta directamente al operador.

Tor comenzó como «the onion router», y un servicio onion es un servicio al que sólo se puede acceder a través de Tor. Una dirección de versión 3 tiene 56 caracteres seguidos de .onion. Esos caracteres contienen la clave pública ed25519 del servicio, una suma de comprobación y un byte de versión, codificados en base32. Las direcciones de versión 2, de 16 caracteres, se retiraron de la red en 2021. Por tanto, todo lo que genere hoy será v3. La dirección es la clave, lo que tiene dos consecuencias. La conexión se cifra y autentica de extremo a extremo sin una autoridad certificadora, y perder el archivo de clave implica perder la dirección de forma permanente.

Su servidor nunca acepta una conexión entrante. Tor selecciona varios relays como puntos de introducción, carga un descriptor firmado en los servidores de directorio y se encuentra con cada visitante en un relay de rendezvous elegido por el visitante. Todas esas conexiones son salientes desde su servidor. No hay ningún puerto que abrir ni ningún registro DNS que publicar. Si quiere que el mismo VPS proporcione otro servicio a la red, se trata de una tarea distinta y con una configuración opuesta, porque un puente obfs4 acepta conexiones entrantes de clientes censurados y necesita un puerto accesible.

Instalar tor desde el repositorio de Tor Project

Ubuntu incluye un paquete tor en universe, pero suele mantener una versión cercana a la que estaba vigente cuando se congeló la versión de Ubuntu. El repositorio propio de Tor Project sigue la versión estable actual. Esa es la opción adecuada para el componente que determina si su dirección sigue siendo suya. Que un paquete exista en una distribución no indica que alguien continúe trabajando en él. Conviene comprobarlo en cualquier servicio que aloje usted mismo, ya que Searx no ha recibido commits de código desde 2023, mientras SearXNG continúa ese proyecto.

sudo apt update
sudo apt install -y apt-transport-https gnupg wget
KEYURL=https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
wget -qO- "$KEYURL" | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null

La entrada del repositorio usa el formato deb822, y Suites debe ser el nombre en clave de su versión de Ubuntu. Léalo de /etc/os-release en lugar de escribirlo manualmente. Un nombre en clave incorrecto produce un repositorio que se resuelve correctamente, pero que no contiene paquetes para su versión.

. /etc/os-release
sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null <<EOF
Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: $VERSION_CODENAME
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
EOF
sudo apt update
sudo apt install -y tor deb.torproject.org-keyring

El paquete deb.torproject.org-keyring mantiene actualizada la clave de firma. Así, una rotación de claves no interrumpe apt update dentro de un año. Compruebe que tor se haya iniciado y haya alcanzado la red:

tor --version
sudo journalctl -u tor@default -n 20

El journal debería terminar con Bootstrapped 100% (done): Done. Si tor se queda en Bootstrapped 10%, no tiene una ruta de salida. Compruebe el firewall de red de su proveedor y sus propias reglas de salida: sudo ufw status verbose debería mostrar allow (outgoing) como valor predeterminado.

A partir de aquí importan dos nombres. El paquete ejecuta tor con el usuario debian-tor, y la unidad en ejecución es tor@default.service, porque tor.service en Debian y Ubuntu es un wrapper alrededor de la instancia. Solicite el estado y los registros usando el nombre de la instancia para obtener siempre el proceso real.

Configurar el servicio onion en torrc

Añada dos líneas a /etc/tor/torrc:

HiddenServiceDir /var/lib/tor/onion_site/
HiddenServicePort 80 127.0.0.1:8080

HiddenServiceDir es donde tor conserva las claves y la dirección de este servicio. No lo cree manualmente. Tor lo crea al iniciarse, con el propietario y los permisos que necesita. Si crea el directorio como root, se producirá el primer fallo de la lista de errores siguiente.

HiddenServicePort tiene dos partes. Confundirlas es el error inicial más habitual. El primer número es el puerto al que se conecta el visitante dentro del túnel. Por eso 80 es el valor esperado y no hay motivo para cambiarlo. La segunda parte es la dirección local a la que tor reenvía ese tráfico. Un HiddenServicePort 80 sin dirección reenvía a 127.0.0.1:80. Especificar la dirección y usar un puerto alto mantiene el vhost onion separado de cualquier servicio que ya escuche en 80.

sudo systemctl restart tor@default
sudo ls -l /var/lib/tor/onion_site/

El listado debe contener hostname, hs_ed25519_public_key, hs_ed25519_secret_key y un directorio authorized_clients vacío.

Lee tu dirección .onion

sudo cat /var/lib/tor/onion_site/hostname

Aparece una línea: 56 caracteres base32 y .onion. Esa cadena es la identidad completa del sitio. Nadie la asigna, nadie puede transferirla y nadie puede quitártela mientras conserves el archivo de clave. Cópiala ahora, porque todas las configuraciones siguientes la necesitan. El resto de esta guía la escribe como <your-address>.onion.

Servir el sitio desde nginx enlazado a 127.0.0.1

sudo apt install -y nginx
sudo install -d -m 755 /srv/onion

Escriba /etc/nginx/sites-available/onion:

server {
    listen 127.0.0.1:8080;
    server_name <your-address>.onion;

    root /srv/onion;
    index index.html;

    server_tokens off;
    etag off;
    access_log off;
    error_log /var/log/nginx/onion.error.log error;
}
echo '<h1>hello from the onion</h1>' | sudo tee /srv/onion/index.html
sudo ln -s /etc/nginx/sites-available/onion /etc/nginx/sites-enabled/onion
sudo nginx -t
sudo systemctl reload nginx

Ahora compruebe dos aspectos desde el servidor. El primero es que nginx responde para el nombre onion, que es la cabecera Host exacta que enviará tor:

curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/

El segundo es que responde en esa dirección y en ninguna otra:

sudo ss -tlnp | grep 8080

La columna de dirección debe mostrar 127.0.0.1:8080. Si muestra 0.0.0.0:8080 o *:8080, el sitio onion también está disponible en Internet público, lo que constituye el primer elemento de la lista de fugas. Una línea listen 8080; sin dirección enlaza todas las interfaces, y ese es el valor predeterminado.

Abra la dirección en Tor Browser. La primera carga tarda unos segundos mientras el cliente obtiene el descriptor y construye un circuito de encuentro.

La documentación del propio Tor Project recomienda usar un socket Unix en lugar de un puerto de loopback: HiddenServicePort 80 unix:/var/run/tor/onion_site.sock, con nginx escuchando en esa ruta. No se puede acceder a un socket desde otro host, aunque el equipo añada más adelante una segunda interfaz. El coste son los permisos del archivo, porque nginx crea el socket y tor se conecta a él como debian-tor. Por tanto, ambos usuarios deben tener acceso al directorio. El loopback con una salida ss verificada es más sencillo de configurar correctamente, y es el método que se presupone en el resto de esta guía.

Con el sitio en loopback, el equipo no necesita ninguna regla de entrada para ese servicio. Mantenga abierto el puerto 22 para usted y deniegue el resto (valores predeterminados de ufw recomendados para un VPS). Recuerde que un firewall no corrige un servicio que se enlaza a 0.0.0.0; sólo filtra los paquetes que llegan al firewall. Los contenedores hacen esto más importante, porque publicar un puerto de Docker escribe reglas de iptables antes que ufw, de modo que -p 8080:80 coloca el backend onion en la IP pública aunque ufw siga informando que el puerto está denegado. Publique los puertos de los contenedores como -p 127.0.0.1:8080:80.

Filtraciones que desanonimizan un sitio onion

Tor oculta dónde está el servidor. Tor no oculta nada de lo que el servidor publica. Cada elemento siguiente es información que publica su propia pila. Leer cada herramienta de privacidad según sus límites reales, y no según su reputación, es el mismo hábito que permite saber qué oculta un SearXNG autohospedado: oculta su IP de los motores de búsqueda y nada más.

El mismo sitio responde en su IP pública

Este es el caso que suele pasar inadvertido. Los escáneres indexan continuamente la respuesta HTTP de cada dirección enrutable, y esos resultados son públicos y se pueden buscar. Si sirve la misma página en su IP pública y en su dirección onion, asociarlas requiere una sola consulta: mismo título, mismo hash del favicon, mismo ETag y mismo orden de cabeceras. La línea listen 127.0.0.1:8080; anterior es la solución. Verifíquelo desde otra máquina, no desde el servidor:

curl -sv --max-time 5 http://<your-public-ip>:8080/

Connection refused o un tiempo de espera agotado es el resultado correcto. Cualquier HTML significa que el sitio es público. Si el equipo también ejecuta un sitio clearnet, asigne a ese vhost su propio root y mantenga un bloque default_server explícito en el listener público, para que una cabecera Host que no coincida nunca termine en el vhost onion.

Banners de versión

curl -sI http://127.0.0.1:8080/ | grep -i '^server'

Un nginx predeterminado responde Server: nginx/1.24.0. Esa cadena de versión, junto con el orden exacto de las demás cabeceras, es una huella que permite asociar su servicio onion con su host clearnet. server_tokens off; la reduce a Server: nginx. No elimina la cabecera, y nginx no tiene una directiva integrada para hacerlo, por lo que el módulo headers-more es la opción habitual si quiere eliminarla. PHP añade X-Powered-By hasta que configure expose_php = Off. etag off; debe incluirse en la misma lista, porque nginx construye el ETag a partir de la hora de modificación y el tamaño de un archivo. Por eso, los mismos archivos copiados en dos servidores entregan el mismo ETag en ambos.

URLs absolutas que apuntan a su dominio clearnet

Una etiqueta rel="canonical", un og:url de Open Graph, un feed RSS, un sitemap, un correo de restablecimiento de contraseña o una URL de logotipo codificada directamente. Cualquiera de ellos nombra el sitio clearnet dentro de una página servida a través de onion. Use rutas relativas a la raíz, como /static/logo.svg, y haga que la aplicación lea su URL base del host de la petición en lugar de usar una constante. Las redirecciones son el mismo error en otro lugar: return 301 https://example.com$request_uri; en un bloque catch-all envía al visitante onion a su dominio real, y la cabecera Location le entrega la respuesta directamente.

Un certificado TLS compartido con el sitio clearnet

Una dirección onion se autentica por sí misma, porque la dirección es la clave pública. Por tanto, http:// mediante una conexión onion ya está cifrado de extremo a extremo, y Tor Browser lo trata como un contexto seguro. Instalar el certificado existente en el vhost onion publica la relación entre ambos sitios, porque todos los certificados de confianza pública se registran en los registros de Certificate Transparency, que son públicos, permanentes y se pueden buscar por nombre. Mantenga los certificados de Let's Encrypt en el vhost clearnet y deje el vhost onion en HTTP sin cifrar.

Fuentes y analítica de terceros

Una fuente de un CDN (red de distribución de contenido) o un script de analítica. El navegador del visitante obtiene cada recurso directamente, por lo que el tercero sabe que alguien cargó su página y, normalmente, qué página fue. Además, los niveles de seguridad más estrictos de Tor Browser bloquean la petición y dejan el diseño incompleto. Aloje usted mismo todos los recursos que necesite la página.

Incoherencia en la cabecera Host

Si server_name no coincide con la cabecera Host que envía tor, nginx recurre al servidor predeterminado para esa dirección de escucha. En un equipo con un solo vhost esto pasa inadvertido, porque el único bloque de servidor también es el predeterminado. Si después añade un vhost clearnet, las peticiones onion pueden empezar a llegar a él, con sus etiquetas canónicas y sus redirecciones. Vuelva a ejecutar la comprobación curl -H 'Host: ...' después de cada cambio en nginx y busque su dominio real en el resultado:

curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/ | grep -o 'https\?://[^"]*' | sort -u

Saber qué proceso es propietario de cada socket constituye gran parte de este trabajo (cómo funcionan los puertos y los sockets de escucha en Linux).

Lo que queda en los registros

Cada petición llega desde 127.0.0.1, por lo que nginx no tiene ninguna dirección del visitante que registrar y access_log off; no le cuesta nada. La aplicación situada detrás es otro asunto, ya que usted debe gestionar los pedidos, las direcciones de correo electrónico y los metadatos de los archivos subidos. Sus propios hábitos también cuentan: administrar el equipo mediante un acceso no reforzado queda fuera de cualquier protección de Tor. Por tanto, considere el refuerzo de SSH en el mismo VPS parte de esta configuración.

Haga una copia de seguridad de la clave privada, porque es la dirección

/var/lib/tor/onion_site/hs_ed25519_secret_key es el servicio. No hay registrador ni recuperación. Si la pierde, la dirección desaparece. Si copia la clave, cualquiera que tenga esa copia puede publicar su propio contenido en su dirección, y usted no podrá revocar nada.

sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -czf onion-keys.tgz onion_site
sudo chmod 600 onion-keys.tgz
sudo systemctl start tor@default

Cifre ese archivo (gpg -c onion-keys.tgz) y transfiéralo fuera del servidor. Para restaurarlo en un VPS nuevo, sólo necesita el archivo y la propiedad que espera tor:

sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -xzf onion-keys.tgz
sudo chown -R debian-tor:debian-tor /var/lib/tor/onion_site
sudo chmod 700 /var/lib/tor/onion_site
sudo systemctl start tor@default
sudo cat /var/lib/tor/onion_site/hostname

La misma dirección vuelve a estar disponible en el hardware nuevo uno o dos minutos después de que tor vuelva a publicar el descriptor. Esa es toda la migración: no hay que cambiar DNS ni volver a emitir el certificado.

Onion-Location cuando el sitio también está disponible en la clearnet

Si la dirección onion es una comodidad y no un secreto, publíquela desde el vhost de la clearnet:

add_header Onion-Location http://<your-address>.onion$request_uri;

Tor Browser muestra entonces un botón .onion available en la barra de direcciones y ofrece cambiar al sitio onion. El encabezado sólo se tiene en cuenta cuando la página de la clearnet se sirve mediante HTTPS y el valor es una URL onion válida.

Aquí hay una regla de nginx importante. Las directivas add_header se heredan en un bloque location sólo cuando ese bloque no declara ninguna propia, por lo que un location con su propio add_header descarta silenciosamente Onion-Location. Repítalo allí o mantenga todos los encabezados de respuesta en un único lugar. Publicar este encabezado vincula deliberadamente ambos sitios. Es correcto para un espejo, pero incorrecto para cualquier sitio que deba permanecer sin vincular.

Direcciones personalizadas

mkp224o genera pares de claves hasta que uno produce una dirección que comienza con el prefijo solicitado. Es una búsqueda por fuerza bruta, así que no hay nada que configurar aparte del prefijo y del tiempo que esté dispuesto a esperar.

sudo apt install -y git gcc libc6-dev libsodium-dev make autoconf
git clone https://github.com/cathugger/mkp224o
cd mkp224o
./autogen.sh
./configure --enable-amd64-51-30k
make
./mkp224o -d onionkeys blog

Cada coincidencia se guarda en onionkeys/<address>.onion/, que contiene hostname y hs_ed25519_secret_key. Instálela deteniendo tor, copiando ese directorio sobre HiddenServiceDir y aplicando después los mismos chown y chmod 700 que en la restauración anterior.

La longitud del prefijo determina todo el coste. La dirección usa base32, por lo que cada carácter adicional multiplica por 32 el número esperado de claves. Un prefijo corto se genera en un portátil. Uno largo no termina en ningún equipo que tenga. Un prefijo personalizado también acostumbra a los lectores a reconocer sólo los primeros caracteres en lugar de la dirección completa. Ese es el hábito en el que se basan las copias de sitios onion usadas para phishing.

Modos de fallo, con las cadenas que verá

No hay ningún archivo hostname después del reinicio. Tor no se inició o rechazó el directorio. sudo journalctl -u tor@default -n 50 lo indica:

/var/lib/tor/onion_site/ is not owned by this user (debian-tor, 108) but by root (0). Perhaps you are running Tor as the wrong user?

Así se ve un directorio creado manualmente. Corrija el propietario y los permisos, o elimine el directorio y deje que tor lo cree.

Tor Browser muestra Onionsite Not Found (0xF0). El cliente no pudo obtener un descriptor. Por tanto, para la red no hay nada publicado en esa dirección. Confirme que tor esté ejecutándose y haya completado el arranque, compare carácter por carácter la dirección que escribió con sudo cat /var/lib/tor/onion_site/hostname y, después, compruebe el reloj. Tor necesita una hora exacta para publicar y validar descriptores, y timedatectl debería informar System clock synchronized: yes.

La dirección se resuelve, pero la página nunca carga. Tor completó el encuentro y después falló en el último salto, entre tor y nginx. Ese salto es local, por lo que el registro de tor permanece en silencio. Ejecute curl -sI http://127.0.0.1:8080/ en el servidor. Connection refused significa que nginx está detenido o escucha en una dirección distinta de aquella a la que apunta HiddenServicePort.

La página carga y todos los enlaces llevan a su dominio real. Hay URL absolutas en las plantillas. Ejecute la comprobación de grep -o 'https\?://[^"]*' anterior y corrija lo que muestre antes de compartir la dirección.

Funciona y después deja de hacerlo tras un reinicio. Reinicie el equipo una vez de forma intencionada antes de confiar en el sitio. Después, ejecute sudo systemctl status tor@default y sudo systemctl status nginx. Un servicio iniciado manualmente tiene el mismo aspecto que uno habilitado hasta que se reinicia el equipo.

FAQ

¿Necesito abrir un puerto en el firewall para un servicio onion de Tor?

No. El daemon de tor sólo establece conexiones salientes con los servidores de directorio, sus puntos de introducción y cada relay de encuentro, por lo que no se necesita ninguna regla entrante y el propio servidor web escucha en 127.0.0.1. Mantenga ufw con la denegación predeterminada del tráfico entrante y permita SSH. Esta misma propiedad permite que un servicio onion funcione desde una máquina detrás de NAT (traducción de direcciones de red), incluso sin una IP pública.

¿Por qué no puedo acceder a mi dirección .onion en Tor Browser?

Compruebe el problema desde el servidor hacia fuera. sudo journalctl -u tor@default -n 50 debería mostrar Bootstrapped 100% (done): Done. Después, curl -sI http://127.0.0.1:8080/ en el servidor debería devolver una línea de estado. A continuación, compare la dirección que introdujo con el archivo hostname, porque un solo carácter incorrecto corresponde simplemente a otro servicio. Onionsite Not Found (0xF0) significa que no se encontró ningún descriptor para la dirección. Normalmente esto indica que tor no está en ejecución o que el reloj del sistema es incorrecto.

¿Puedo mover mi sitio onion a un servidor nuevo y conservar la misma dirección?

Sí. La dirección se deriva de hs_ed25519_secret_key. Copie todo HiddenServiceDir al nuevo equipo, asígnele la propiedad de debian-tor y el modo 700, y arranque tor. La dirección volverá a estar activa cuando se publique de nuevo el descriptor. No hay ningún registro DNS que actualizar. Si pierde ese archivo, la dirección no se puede recuperar. Haga una copia de seguridad cifrada fuera del servidor el mismo día que lo cree.

¿Necesita un sitio onion un certificado HTTPS?

No. La dirección de 56 caracteres es la clave pública del servicio, por lo que la conexión ya está cifrada y autenticada de extremo a extremo. Tor Browser trata http:// en un nombre .onion como un contexto seguro. Reutilizar el certificado de su sitio clearnet en el vhost onion es peor que no hacer nada, porque los registros de Certificate Transparency son públicos y registran de forma permanente qué nombres comparten un certificado. La única razón para comprar un certificado para un nombre .onion es obtener la garantía de marca de una CA que los emita; ese vínculo es público por diseño.