SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-21

Thêm header Onion-Location vào site nginx

Cấu hình header Onion-Location trong nginx để Tor Browser hiện địa chỉ onion, rồi xử lý redirect và asset bên thứ ba có thể làm lộ clearnet.

Header Onion-Location làm gì

Header Onion-Location là một dòng trong clearnet vhost, dùng để thông báo địa chỉ onion cho Tor Browser. Khi truy cập https://example.com qua Tor, khách truy cập sẽ thấy một nút màu tím trên thanh địa chỉ với nội dung .onion available. Nhấp một lần sẽ chuyển họ sang onion service của bạn. Đây chỉ là cơ chế phát hiện địa chỉ. Header này không tạo onion service và cũng không che giấu thông tin nào về bạn.

Hướng dẫn này giả định cả hai phần đã tồn tại. Bạn có một site trên VPS phía sau nginx và một onion service v3 đang hoạt động, trỏ đến site đó. Nếu chưa có phần thứ hai, hãy thiết lập trước: host site onion trên VPS trình bày các dòng torrc và file hostname đầu tiên. Phần tiếp theo hướng dẫn cách kết nối hai phần này mà không để lộ phần này qua phần kia.

Điều kiện để Tor Browser chấp nhận header

Tor Project nêu 3 điều kiện. Cả 3 điều kiện đều phải được đáp ứng, nếu không pill sẽ không xuất hiện.

  • Giá trị Onion-Location phải là URL hợp lệ với scheme http: hoặc https: và hostname .onion.
  • Webpage chứa header phải được cung cấp qua HTTPS.
  • Webpage chứa header không được là onion site.

Điều kiện thứ 2 thường gây nhầm lẫn. Điều kiện thứ 3 giải thích vì sao bạn không bao giờ đặt header này trên onion vhost. Có một quy tắc thứ 4 không được nêu trong tài liệu hướng dẫn dạng prose nhưng có trong implementation: Tor Browser chỉ xử lý header trên tài liệu top-level. Code so sánh load target với document trước khi thực hiện bất kỳ hành động nào. Vì vậy, header trả về trên stylesheet, image hoặc API response sẽ bị bỏ qua.

Mặc định, browser hiển thị pill và chờ người dùng click. Người dùng muốn chuyển hướng tự động có thể bật tùy chọn này trong Settings, sau đó vào Privacy and Security rồi Onion Services. Tại đây, tùy chọn "Prioritize .onion sites when known" có thể đặt thành "Always". Bạn không thể ép browser thực hiện việc này từ phía server. Hãy xem header này là một đề xuất, không phải redirect.

Thêm header Onion-Location trong nginx

Header này phải nằm trong server block xử lý TLS cho domain clearnet của bạn. Nếu đặt nó trong block cổng 80 thì sẽ không có tác dụng, vì block đó chỉ chuyển hướng và quy tắc hai loại trừ header được định nghĩa trên trang HTTP thuần.

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

    root /srv/example.com/public;
}

$request_uri chứa path và query string, để người dùng đang ở https://example.com/guides/tor được chuyển đến cùng path trên onion. Nếu bỏ nó, mọi visitor sẽ vào trang chủ onion thay vì trang họ đang xem.

always cần thiết vì nginx có một giới hạn đã được ghi nhận. add_header chỉ thêm field khi response code là 200, 201, 204, 206, 301, 302, 303, 304, 307 hoặc 308. Trang 404 của bạn là một entry point thực sự từ kết quả tìm kiếm. Nếu không có always, trang đó hoàn toàn không có header.

Bẫy thứ hai trong nginx là cơ chế kế thừa, và lỗi này xảy ra một cách im lặng. Các directive add_header chỉ được kế thừa từ cấp cấu hình trước nếu cấp hiện tại không có directive add_header nào. Vì vậy, một block location /assets/ { add_header Cache-Control ...; } sẽ loại bỏ Onion-Location ở cấp server đối với mọi URL bên trong block đó. Nếu bạn đặt header riêng cho từng location ở bất kỳ đâu, hãy lặp lại dòng Onion-Location bên trong từng block đó. Cách nginx chọn server block và location block đáng để đọc nếu cơ chế này còn mới với bạn.

Reload nginx rồi kiểm tra cả một trang bình thường và một trang không tồn tại:

sudo nginx -t && sudo systemctl reload nginx
curl -sI https://example.com/ | grep -i onion-location
curl -sI https://example.com/no-such-page | grep -i onion-location

Cả hai command phải in ra một dòng onion-location:. Kết quả của command thứ hai chứng minh rằng always đang hoạt động. Nếu command thứ hai không in gì, nghĩa là flag bị thiếu hoặc một block location đang ghi đè directive.

Thẻ meta HTML khi không thể đặt header

Các host tĩnh và một số dashboard CDN không cho phép thêm response header tùy ý. Cùng một giá trị vẫn dùng được dưới dạng phần tử meta trong phần head của tài liệu, vì trình duyệt đọc dữ liệu header của tài liệu theo cùng một cách, bất kể dữ liệu đến qua HTTP hay nằm trong thẻ http-equiv.

<meta http-equiv="onion-location" content="http://<your-onion-address>.onion" />

Ba yêu cầu vẫn được áp dụng. Trang chứa thẻ phải dùng HTTPS và không được là onion. Điểm khác biệt là thẻ chứa một địa chỉ cố định không có path, vì không có biến phía server để mở rộng. Mọi trang chứa thẻ này đều cung cấp trang chủ onion. Đây là cái giá của phương án dự phòng, vì vậy hãy ưu tiên header khi bạn kiểm soát server.

Cung cấp onion bằng vhost nginx riêng

Website clearnet và onion không được dùng chung một server block. Tor Browser gửi Host: <your-onion-address>.onion. Nếu không có server block nào nhận hostname đó, nginx sẽ fallback về default server. Đây là clearnet vhost của bạn, nên mọi URL do vhost này tạo ra đều chứa domain của bạn.

Trỏ hidden service đến một port chỉ có interface loopback lắng nghe:

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

Sau đó tạo vhost riêng cho port này:

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

    absolute_redirect off;
    port_in_redirect off;

    root /srv/example.com/public;
}

listen 127.0.0.1:8080 giữ vhost này ngoài public IP. Vì vậy, người quét địa chỉ VPS không thể truy cập nó rồi so sánh từng byte với bản clearnet. absolute_redirect off yêu cầu nginx tạo các giá trị Location tương đối. Nhờ đó, redirect thêm dấu gạch chéo cuối vào directory sẽ trả về Location: /guides/ thay vì URL đầy đủ. nginx vốn đã tạo redirect tuyệt đối từ header Host thay vì từ server_name, vì server_name_in_redirect mặc định là off. Tuy nhiên, redirect tương đối loại bỏ hoàn toàn vấn đề này.

Vì sao trang onion vẫn chuyển khách truy cập đến site clearnet?

nginx hiếm khi là nơi làm lộ địa chỉ. Ứng dụng mới là nguyên nhân. Bất kỳ thành phần nào tạo absolute URL từ địa chỉ site đã cấu hình đều sẽ dùng domain của bạn, bất kể request được vhost nào xử lý.

  • Thẻ link rel="canonical" trỏ đến https://example.com/.... Đây là trường hợp phổ biến nhất. Nó hiển thị chính xác trang clearnet cho bất kỳ ai xem source.
  • Redirect do framework tạo ra thay vì nginx, chẳng hạn SECURE_SSL_REDIRECT của Django hoặc các tùy chọn homesiteurl của WordPress.
  • og:url và các meta tag social card khác.
  • Các mục trong sitemap và RSS. Theo đặc tả, chúng luôn dùng absolute URL.
  • Các trang lỗi của ứng dụng. Chúng thường có link “quay lại trang chủ” được tạo từ cùng setting đó.

Cách sửa phụ thuộc vào stack của bạn và không có cách chung cho mọi trường hợp. Cách kiểm tra thì giống nhau. Dùng Tor để fetch trang onion, sau đó tìm domain của bạn trong response.

curl -s --socks5-hostname 127.0.0.1:9050 http://<your-onion-address>.onion/ \
  | grep -i 'example\.com'

--socks5-hostname gửi tên đó đến cổng SOCKS của Tor để phân giải. Việc này bắt buộc vì không có thành phần nào trên máy bạn có thể phân giải tên .onion ở local. Cổng 9050 là mặc định của tor daemon được cài từ package. Kết quả rỗng nghĩa là đạt. Bất kỳ kết quả nào cũng cho biết trang đó đang gửi domain clearnet của bạn đến mọi khách truy cập onion. Chạy kiểm tra với trang chủ, sau đó với một URL tạo ra lỗi 404.

Hãy kiểm tra riêng chuỗi redirect vì body của redirect thường rỗng:

curl -sI --socks5-hostname 127.0.0.1:9050 http://<your-onion-address>.onion/guides \
  | grep -i '^location'

Giá trị Location chứa example.com nghĩa là redirect đang đẩy khách truy cập onion quay lại clearnet thông qua một exit node, dù request đó được họ tin là vẫn nằm trong Tor.

Không đưa certificate của clearnet lên onion

Địa chỉ onion v3 được tạo từ public key của chính service đó. Vì vậy, Tor xác thực và mã hóa circuit đến đúng service này trước khi gửi bất kỳ HTTP request nào. Plain HTTP bên trong onion service là cấu hình thông thường. Nó không giống plain HTTP qua Internet.

Nếu bạn tạo onion vhost bằng cách sao chép vhost clearnet, bạn cũng sao chép ssl_certificate. Khi đó, onion sẽ cung cấp certificate có subject alternative names liệt kê example.com. Hai vấn đề sẽ xảy ra. Trình duyệt báo lỗi tên không khớp vì URL là địa chỉ onion nhưng certificate không bao phủ địa chỉ đó. Ngoài ra, mọi visitor nhấp tiếp đều nhận được một xác nhận có chữ ký rằng hai site này nằm trên cùng một máy. Đặt onion vhost trong file riêng với server_name riêng. Cách này cũng giúp tách nó khỏi Certbot nginx plugin, vì plugin đó chỉnh sửa server block khớp với domain mà bạn yêu cầu cấp certificate.

Cài gói Tor nào và bảo vệ service key

Gói tor trong kho Ubuntu đáp ứng nhu cầu này và không cần thiết lập thêm. Gói này chậm hơn series stable hiện tại, nên với service dự định chạy lâu dài, hãy dùng Debian repository của Tor Project và để apt nâng cấp nó cùng các gói khác. Tính đến tháng 8 năm 2026, các bước được tài liệu hóa như sau:

sudo apt install apt-transport-https
wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc \
  | gpg --dearmor \
  | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null

Ghi /etc/apt/sources.list.d/tor.sources, thay suite bằng release codename của bạn lấy từ lsb_release -c:

Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: noble
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
sudo apt update
sudo apt install tor deb.torproject.org-keyring

Gói deb.torproject.org-keyring luôn cập nhật signing key, để repository không ngừng xác minh sau một năm. Chọn một nguồn và chỉ dùng nguồn đó. Gói trong archive và gói từ repository có version khác nhau. apt sẽ chuyển đổi giữa chúng khi upgrade nếu cả hai nguồn đều được bật.

HiddenServiceDir chứa service identity của bạn. File hs_ed25519_secret_key trong thư mục đó chính là onion address của bạn, vì địa chỉ này là public half của key pair đó. Nếu mất file, địa chỉ sẽ mất vĩnh viễn vì không có authority nào có thể cấp lại. Nếu sao chép file vào nơi không an toàn, bất kỳ ai giữ bản sao cũng có thể chạy onion service của bạn.

tor từ chối sử dụng directory mà user khác có thể đọc. Hãy kiểm tra mode và owner trước khi kiểm tra bất kỳ thứ gì khác:

sudo ls -ld /var/lib/tor/onion_site
sudo -u debian-tor cat /var/lib/tor/onion_site/hostname

Kết quả listing phải là drwx------, với owner và group là debian-tor trên Debian và Ubuntu. Nếu quyền rộng hơn, tor sẽ ghi một dòng như Permissions on directory /var/lib/tor/onion_site/ are too permissive. vào log và service sẽ không khởi động. Sửa bằng sudo chown -R debian-tor:debian-tor /var/lib/tor/onion_site, sau đó chạy sudo chmod 700 /var/lib/tor/onion_site, restart bằng sudo systemctl restart tor và đọc kết quả bằng sudo journalctl -u tor@default -n 30.

Hãy backup directory đó giống như backup private key: lưu ngoài máy và mã hóa. Không bao giờ commit nó vào repository chứa website của bạn. Nếu cùng máy đó cũng cần administrative access qua Tor, truy cập SSH qua onion service sẽ tách biệt rõ ràng hơn so với việc expose management path trên public site.

Analytics và asset bên thứ ba làm lộ nhiều hơn header

Đây là phần quan trọng nhất và hoàn toàn không liên quan đến Onion-Location. Mỗi asset bên thứ ba được page của bạn tham chiếu đều tạo ra một request mà browser của người truy cập gửi ra khỏi onion rồi quay lại clearnet thông qua một exit node. Font từ public CDN, analytics script được host bên ngoài, trình phát video được nhúng hoặc comment widget đều cho bên thứ ba đó biết rằng có người đang tải page của bạn trong một session mà người đọc đã chủ động định tuyến qua Tor.

Có 2 hệ quả. Bên thứ ba biết về lượt truy cập đó. Ngoài ra, vì bản sao clearnet của bạn cũng tải các asset giống nhau từ cùng provider, bất kỳ ai quan sát được một trong 2 phía đều có thể dễ dàng liên kết 2 property này.

Phục vụ mọi thứ từ cùng một origin. Tự host font. Xóa analytics tag được host bên ngoài hoặc chuyển nó sang máy của bạn, để analytics tự host trên VPS giữ request ở bên trong onion. Hãy dự kiến rằng các thiết lập mặc định của Tor Browser sẽ chặn hoặc làm giảm nhiều dữ liệu mà bất kỳ analytics tool nào cố thu thập. Đây là kết quả đúng. Nếu một page không thể hoạt động khi thiếu script bên thứ ba, đừng publish page đó trên onion.

Liệt kê những gì một page thực sự tải:

curl -s --socks5-hostname 127.0.0.1:9050 http://<your-onion-address>.onion/ \
  | grep -oE '(src|href)="https?://[^"]+"' | sort -u

Mỗi dòng lệnh này in ra là một absolute URL mà page của bạn yêu cầu browser fetch. Bất kỳ URL nào không phải địa chỉ onion của bạn đều là một outbound clearnet request mà bạn đang yêu cầu người đọc thực hiện thay mình.

Mô hình đe dọa, nói thẳng

Onion-Location giúp người dùng dễ tìm onion service hơn. Chỉ vậy thôi. Nó không ẩn danh bạn với tư cách operator, vì clearnet domain của bạn vẫn có thông tin đăng ký registrar, DNS record, certificate được công bố trong Certificate Transparency logs và tài khoản VPS chứa thông tin thanh toán của bạn. Nó cũng không ẩn danh onion service, vì bạn vừa công khai lâu dài từ clearnet domain rằng hai địa chỉ này là cùng một site. Lợi ích thuộc về người đọc: người truy cập qua Tor có thể tiếp tục ở trong mạng Tor, không có exit node trên đường truyền và không cần DNS lookup cho domain của bạn. Nếu mục tiêu của bạn là một onion service mà không ai có thể liên hệ ngược đến bạn, đừng publish header này và đừng chạy hai bản sao trên cùng một máy.

Có 2 câu hỏi liên quan thường được đặt ra ở đây, và mỗi câu có câu trả lời riêng. Sự khác nhau giữa Tor và VPN quyết định bạn dùng gì cho traffic của chính mình. Đây là một quyết định riêng với nội dung bạn publish. Nếu người đọc trong mạng bị kiểm duyệt không thể truy cập clearnet site, họ sẽ không bao giờ thấy header này. Khi đó, bridge và pluggable transport quan trọng hơn mọi nội dung khác trên trang này.

Xác minh toàn bộ cấu hình một lần

Chạy các lệnh sau theo thứ tự. Mỗi lệnh đều có kết quả để bạn kiểm tra.

  1. curl -sI https://example.com/ | grep -i onion-location in header.
  2. Chạy cùng lệnh với một URL trả về 404 cũng in header đó.
  3. curl -s --socks5-hostname 127.0.0.1:9050 http://<your-onion-address>.onion/ trả về trang của bạn.
  4. Grep output đó với domain clearnet của bạn không trả về kết quả nào.
  5. Tor Browser trên https://example.com hiển thị pill .onion available.

Nếu bước 1 đến bước 4 đều đạt nhưng bước 5 không đạt, nguyên nhân gần như luôn nằm ở nơi header được gửi ra, không phải ở bản thân header. Xác nhận browser thực sự đã tải trang HTTPS, không phải một redirect đã được cache, sau đó chạy curl -sI với đúng URL bạn đã mở, vì một block location trên path cụ thể đó có thể loại bỏ directive cấp server.

FAQ

Vì sao Tor Browser không hiển thị pill ".onion available"?

Trước tiên, hãy kiểm tra 3 yêu cầu đã được ghi trong tài liệu. Giá trị phải là một URL đầy đủ với scheme http: hoặc https: và host .onion. Vì vậy, địa chỉ không có scheme sẽ bị lỗi nhưng không in ra thông báo. Trang phải được phân phối qua HTTPS, nên header đặt trong block redirect của cổng 80 sẽ không bao giờ được đọc. Trang đó cũng không được là onion. Sau đó, kiểm tra nginx: mọi add_header bên trong block location khớp sẽ loại bỏ toàn bộ add_header ở cấp server. Nếu không có flag always, header sẽ không xuất hiện trong các response 404 và 500. Chạy curl -sI với đúng URL đã tải trong browser và xác nhận header thực sự có trên wire.

Tôi có cần chứng chỉ TLS cho site onion không?

Không. Địa chỉ onion v3 được tạo từ public key của service. Vì vậy, circuit được xác thực với đúng service đó và được mã hóa end-to-end trước khi gửi bất kỳ HTTP request nào. Dùng HTTP thuần bên trong onion service là cấu hình thông thường. Điều cần tránh là dùng certificate clearnet trên onion. Danh sách subject alternative names của certificate chứa domain của bạn. Điều này khiến browser hiển thị cảnh báo tên không khớp và xác nhận với mọi visitor rằng 2 site chạy trên cùng một máy.

Việc công khai Onion-Location có làm site của tôi ẩn danh không?

Không. Header này là tuyên bố công khai từ domain clearnet của bạn rằng một địa chỉ onion cụ thể thuộc về bạn, và bất kỳ ai cũng có thể fetch nó. Lợi ích thuộc về người đọc. Họ có thể chuyển sang onion, loại exit node và DNS lookup khỏi đường đi của mình. Với tư cách operator, bạn không có thêm tính ẩn danh nào và vĩnh viễn liên kết 2 địa chỉ này. Onion service không được phép truy vết về bạn phải được công khai ở nơi khác, trên phần cứng không dùng chung bất kỳ thứ gì với site clearnet.

Tôi có thể dùng meta tag thay cho HTTP header không?

Có, khi bạn không thể đặt response header. Đây là tình huống thường gặp trên static host. Đặt <meta http-equiv="onion-location" content="http://youraddress.onion" /> trong phần head của document. 3 yêu cầu tương tự vẫn áp dụng, nên trang phải dùng HTTPS và không được là onion. Điểm khác biệt chính là tag chứa một địa chỉ cố định không có path, còn nginx header có thể nối thêm $request_uri và cung cấp cho visitor chính trang đó trên onion thay vì trang chủ.