mysite/public/posts/tor_clearnet_blog/index.html

131 lines
No EOL
66 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod | AlipourIm journeys</title>
<meta name=keywords content="tor,nginx,hugo,papermod,cloudflare,letsencrypt"><meta name=description content="How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy."><meta name=author content="Iman Alipour"><link rel=canonical href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/><link crossorigin=anonymous href=/assets/css/stylesheet.51e3b550fcc0c3f3a10e2d7b70445c6cb21171bed36521d66dc7427208cafbf9.css integrity="sha256-UeO1UPzAw/OhDi17cERcbLIRcb7TZSHWbcdCcgjK+/k=" rel="preload stylesheet" as=style><link rel=icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico><link rel=icon type=image/png sizes=16x16 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-16x16.png><link rel=icon type=image/png sizes=32x32 href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon-32x32.png><link rel=apple-touch-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/apple-touch-icon.png><link rel=mask-icon href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/safari-pinned-tab.svg><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><link rel=alternate hreflang=en href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--code-block-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:url" content="http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/"><meta property="og:site_name" content="AlipourIm journeys"><meta property="og:title" content="Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod"><meta property="og:description" content="How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy."><meta property="og:locale" content="en"><meta property="og:type" content="article"><meta property="article:section" content="posts"><meta property="article:published_time" content="2025-09-19T00:00:00+00:00"><meta property="article:modified_time" content="2025-09-19T00:00:00+00:00"><meta property="article:tag" content="Tor"><meta property="article:tag" content="Nginx"><meta property="article:tag" content="Hugo"><meta property="article:tag" content="Papermod"><meta property="article:tag" content="Cloudflare"><meta property="article:tag" content="Letsencrypt"><meta name=twitter:card content="summary"><meta name=twitter:title content="Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod"><meta name=twitter:description content="How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy."><script type=application/ld+json>{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Posts","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/"},{"@type":"ListItem","position":2,"name":"Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod","item":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/"}]}</script><script type=application/ld+json>{"@context":"https://schema.org","@type":"BlogPosting","headline":"Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod","name":"Running my blog on Tor (.onion) and the Clearnet with Hugo \u002b PaperMod","description":"How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy.","keywords":["tor","nginx","hugo","papermod","cloudflare","letsencrypt"],"articleBody":" TL;DR — The site is built once (Hugo project), published twice:\nOnion: http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ via Tor, no TLS/HSTS, bound to 127.0.0.1:3301. Clearnet: https://blog.alipourimjourneys.ir behind Cloudflare, Lets Encrypt cert, Onion-Location header pointing to the onion mirror. 1) Tor hidden service (onion) basics I used Tors v3 onion services and mapped onion port 80 → my local web server on 127.0.0.1:3301.\nInstall \u0026 configure Tor (Debian/Ubuntu):\nsudo apt update \u0026\u0026 sudo apt install -y tor sudoedit /etc/tor/torrc Add:\nH H i i d d d d e e n n S S e e r r v v i i c c e e D P i o r r t v 8 a 0 r / 1 l 2 i 7 b . 0 t . o 0 r . 1 h : i 3 d 3 d 0 e 1 n _ s i t e / Make sure the directory is owned by Tors user and private:\nsudo mkdir -p /var/lib/tor/hidden_site sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_site sudo chmod 700 /var/lib/tor/hidden_site Important: use the right systemd unit:\n# validate as the Tor user sudo -u debian-tor tor -f /etc/tor/torrc --verify-config # (re)start the real service sudo systemctl enable --now tor@default sudo systemctl restart tor@default Get the onion address:\nsudo cat /var/lib/tor/hidden_site/hostname Quick check (do remote DNS via SOCKS):\ncurl -I --socks5-hostname 127.0.0.1:9050 \"http://$(sudo cat /var/lib/tor/hidden_site/hostname)\" 2) Nginx for the onion (localhost-only) I keep the onion site strictly on localhost: no HTTPS, no redirects, no HSTS. Tor already provides e2e encryption and authenticity.\n/etc/nginx/sites-available/onion-blog:\nserver { listen 127.0.0.1:3301 default_server; server_name .onion 127.0.0.1 localhost; # keep onion simple; no HSTS/redirects here add_header Referrer-Policy no-referrer always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; # (optional) strict CSP so nothing leaks to clearnet # add_header Content-Security-Policy \"default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'\" always; root /srv/hugo/mysite/public-onion; index index.html; location / { try_files $uri $uri/ /index.html; } location ~* \\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)$ { access_log off; add_header Cache-Control \"public, max-age=31536000, immutable\"; try_files $uri =404; } } Enable/reload:\nsudo ln -sf /etc/nginx/sites-available/onion-blog /etc/nginx/sites-enabled/onion-blog sudo nginx -t \u0026\u0026 sudo systemctl reload nginx Gotcha I hit: I had two server blocks on 127.0.0.1:3301, which caused 404s via onion. Make sure only the intended vhost listens there (or mark it default_server). Also, if you ever use a regex in server_name, the syntax is server_name ~* \\.onion$ (note the space after ~*).\n3) Hugo + PaperMod setup (and version bumps) PaperMod now requires Hugo Extended ≥ 0.146.0. I installed the extended binary from the official tarball to avoid Snaps sandbox limitations (Snap cant read /srv paths by default).\n# install Hugo extended (example) VER=0.146.0 cd /tmp wget https://github.com/gohugoio/hugo/releases/download/v${VER}/hugo_extended_${VER}_Linux-amd64.tar.gz tar -xzf hugo_extended_${VER}_Linux-amd64.tar.gz sudo mv hugo /usr/local/bin/hugo hugo version # should say \"extended\" and \u003e= 0.146.0 Create the site and theme:\nsudo mkdir -p /srv/hugo \u0026\u0026 sudo chown -R \"$USER\":\"$USER\" /srv/hugo cd /srv/hugo hugo new site mysite cd mysite git init git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod Config updates: in newer Hugo, paginate is deprecated → use:\n# config/_default/hugo.toml title = \"My Blog\" theme = \"PaperMod\" enableRobotsTXT = true [pagination] pagerSize = 10 [params] defaultTheme = \"auto\" showReadingTime = true showPostNavLinks = true showBreadCrumbs = true showCodeCopyButtons = true I added per-environment overrides so I can build two outputs with different baseURLs:\n# config/clearnet/hugo.toml baseURL = \"https://blog.alipourimjourneys.ir/\" # config/onion/hugo.toml baseURL = \"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/\" 4) Dual builds (clearnet + onion) and one-command deploy I publish the same content twice—once for each base URL and docroot:\ncd /srv/hugo/mysite # clearnet build (served over HTTPS) hugo --minify --environment clearnet -d public-clearnet # onion build (served via Tor) hugo --minify --environment onion -d public-onion sudo systemctl reload nginx Helper script I use:\nsudo tee /usr/local/bin/build-both \u003e/dev/null \u003c\u003c'EOF' #!/usr/bin/env bash set -euo pipefail cd /srv/hugo/mysite hugo --minify --environment clearnet -d public-clearnet hugo --minify --environment onion -d public-onion sudo systemctl reload nginx echo \"Deployed both at $(date)\" EOF sudo chmod +x /usr/local/bin/build-both While editing, I sometimes auto-rebuild on file save:\nsudo apt install -y entr cd /srv/hugo/mysite find content layouts assets static config -type f | entr -r build-both 5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01) Cloudflare is set to Full (strict). I issued a public cert for blog.alipourimjourneys.ir using manual DNS-01 (no API token):\nsudo snap install --classic certbot sudo certbot certonly --manual --preferred-challenges dns -d blog.alipourimjourneys.ir --agree-tos -m you@example.com --no-eff-email Certbot tells you to add a TXT record _acme-challenge.blog.alipourimjourneys.ir. Add it in Cloudflare DNS, verify with dig, then continue. Cert ends up at:\n/ / e e t t c c / / l l e e t t s s e e n n c c r r y y p p t t / / l l i i v v e e / / b b l l o o g g . . a a l l i i p p o o u u r r i i m m j j o o u u r r n n e e y y s s . . i i r r / / f p u r l i l v c k h e a y i . n p . e p m e m Clearnet Nginx vhosts:\n# HTTP → HTTPS redirect (optional; CF usually talks HTTPS to origin anyway) server { listen 80; listen [::]:80; server_name blog.alipourimjourneys.ir; return 301 https://blog.alipourimjourneys.ir$request_uri; } # HTTPS origin (behind Cloudflare) server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name blog.alipourimjourneys.ir; ssl_certificate /etc/letsencrypt/live/blog.alipourimjourneys.ir/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/blog.alipourimjourneys.ir/privkey.pem; # HSTS on clearnet only add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\" always; # Help Tor Browser discover the onion mirror (safe on clearnet) add_header Onion-Location \"http://.onion$request_uri\" always; root /srv/hugo/mysite/public-clearnet; index index.html; location / { try_files $uri $uri/ /index.html; } } Note: dont add HSTS or HTTPS redirects to the onion vhost. Keep onion pure HTTP on localhost.\n6) Troubleshooting I ran into (and fixes) Tor unit confusion: tor.service is a tiny master; the real daemon is tor@default. Use that unit and verify config as debian-tor. Permissions: HiddenServiceDir must be owned by debian-tor and mode 700. Mapping mismatch: If HiddenServicePort 80 127.0.0.1:3301 is set, visit http:/// (no :3301). If you set HiddenServicePort 3301 127.0.0.1:3301, you must use http://:3301/. Curl \u0026 .onion: modern curl refuses .onion unless you use remote DNS via SOCKS: curl -I --socks5-hostname 127.0.0.1:9050 \"http:///\" Two Nginx vhosts on the same port: I had a duplicate server on 127.0.0.1:3301 pointing somewhere else, which caused onion 404s. Keep only one (or mark one default_server). Regex in server_name: if you use it, write server_name ~* \\.onion$ (space after ~*). I fixed an invalid variable name error caused by a missing space. PaperMod with old Hugo: upgraded to extended ≥ 0.146.0. Also updated paginate → [pagination].pagerSize. Snap confinement: Snaps hugo couldnt read /srv (.../void: permission denied). Switched to the tarball build in /usr/local/bin. 7) “Not secure” in Tor Browser? That message can appear because onion uses HTTP. Its OK: Tor provides e2e encryption + onion auth. If you enable HTTPS-Only Mode, add an exception for the site. Also ensure the onion build doesnt reference clearnet resources (scan the built HTML for http(s):// links that arent your onion).\n8) Day-to-day workflow Create content: hugo new posts/my-first-post.md # then set draft: false Publish both: build-both (Optional) Auto on save: find content layouts assets static config -type f | entr -r build-both (Optional) Git push-to-deploy with a bare repo + post-receive hook that runs build-both. Final notes Clearnet gets HTTPS + HSTS and an Onion-Location header. Onion gets no HSTS/redirects, and all assets are self-hosted to avoid mixed content. Serving both worlds from one Hugo repo is easy: two builds, two vhosts, one workflow. ","wordCount":"1264","inLanguage":"en","datePublished":"2025-09-19T00:00:00Z","dateModified":"2025-09-19T00:00:00Z","author":{"@type":"Person","name":"Iman Alipour"},"mainEntityOfPage":{"@type":"WebPage","@id":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/"},"publisher":{"@type":"Organization","name":"AlipourIm journeys","logo":{"@type":"ImageObject","url":"http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/favicon.ico"}}}</script></head><body id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/ accesskey=h title="AlipourIm journeys (Alt + H)">AlipourIm journeys</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)" aria-label="Toggle theme"><svg id="moon" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/ title=Posts><span>Posts</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/phd_journey/ title=PhD_journey><span>PhD_journey</span></a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/about/ title=About><span>About</span></a></li></ul></nav></header><main class=main><article class=post-single><header class=post-header><div class=breadcrumbs><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>Home</a>&nbsp;»&nbsp;<a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/>Posts</a></div><h1 class="post-title entry-hint-parent">Running my blog on Tor (.onion) and the Clearnet with Hugo + PaperMod</h1><div class=post-description>How I hosted a Hugo + PaperMod site both as a Tor onion service and a normal HTTPS site behind Cloudflare, with clean configs, dual builds, and a one-command deploy.</div><div class=post-meta><span title='2025-09-19 00:00:00 +0000 UTC'>September 19, 2025</span>&nbsp;·&nbsp;6 min&nbsp;·&nbsp;Iman Alipour
<span class=post-meta-item><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/tor_clearnet_blog/#isso-thread class=isso-comments-link>Comments</a></span></div></header><div class=toc><details open><summary accesskey=c title="(Alt + C)"><span class=details>Table of Contents</span></summary><div class=inner><ul><li><a href=#1-tor-hidden-service-onion-basics aria-label="1) Tor hidden service (onion) basics">1) Tor hidden service (onion) basics</a></li><li><a href=#2-nginx-for-the-onion-localhost-only aria-label="2) Nginx for the onion (localhost-only)">2) Nginx for the onion (localhost-only)</a></li><li><a href=#3-hugo--papermod-setup-and-version-bumps aria-label="3) Hugo + PaperMod setup (and version bumps)">3) Hugo + PaperMod setup (and version bumps)</a></li><li><a href=#4-dual-builds-clearnet--onion-and-one-command-deploy aria-label="4) Dual builds (clearnet + onion) and one-command deploy">4) Dual builds (clearnet + onion) and one-command deploy</a></li><li><a href=#5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01 aria-label="5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)">5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)</a></li><li><a href=#6-troubleshooting-i-ran-into-and-fixes aria-label="6) Troubleshooting I ran into (and fixes)">6) Troubleshooting I ran into (and fixes)</a></li><li><a href=#7-not-secure-in-tor-browser aria-label="7) “Not secure” in Tor Browser?">7) “Not secure” in Tor Browser?</a></li><li><a href=#8-day-to-day-workflow aria-label="8) Day-to-day workflow">8) Day-to-day workflow</a></li><li><a href=#final-notes aria-label="Final notes">Final notes</a></li></ul></div></details></div><div class=post-content><blockquote><p>TL;DR — The site is built once (Hugo project), <strong>published twice</strong>:</p><ul><li><strong>Onion</strong>: <code>http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/</code> via Tor, no TLS/HSTS, bound to <code>127.0.0.1:3301</code>.</li><li><strong>Clearnet</strong>: <code>https://blog.alipourimjourneys.ir</code> behind Cloudflare, Lets Encrypt cert, <code>Onion-Location</code> header pointing to the onion mirror.</li></ul></blockquote><hr><h2 id=1-tor-hidden-service-onion-basics>1) Tor hidden service (onion) basics<a hidden class=anchor aria-hidden=true href=#1-tor-hidden-service-onion-basics>#</a></h2><p>I used Tors v3 onion services and mapped onion port 80 → my local web server on <code>127.0.0.1:3301</code>.</p><p><strong>Install & configure Tor (Debian/Ubuntu):</strong></p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo apt update <span style=color:#f92672>&amp;&amp;</span> sudo apt install -y tor
</span></span><span style=display:flex><span>sudoedit /etc/tor/torrc
</span></span></code></pre></div><p>Add:</p><div class="goat svg-container"><svg font-family="Menlo,Lucida Console,monospace" viewBox="0 0 344 41"><g transform="translate(8,16)"><path d="M132 8l-8 32" fill="none" stroke="currentcolor"/><path d="M196 8l-8 32" fill="none" stroke="currentcolor"/><path d="M228 8l-8 32" fill="none" stroke="currentcolor"/><text text-anchor="middle" x="0" y="4" fill="currentcolor" style="font-size:1em">H</text><text text-anchor="middle" x="0" y="20" fill="currentcolor" style="font-size:1em">H</text><text text-anchor="middle" x="8" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="8" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="16" y="4" fill="currentcolor" style="font-size:1em">d</text><text text-anchor="middle" x="16" y="20" fill="currentcolor" style="font-size:1em">d</text><text text-anchor="middle" x="24" y="4" fill="currentcolor" style="font-size:1em">d</text><text text-anchor="middle" x="24" y="20" fill="currentcolor" style="font-size:1em">d</text><text text-anchor="middle" x="32" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="32" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="40" y="4" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="40" y="20" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="48" y="4" fill="currentcolor" style="font-size:1em">S</text><text text-anchor="middle" x="48" y="20" fill="currentcolor" style="font-size:1em">S</text><text text-anchor="middle" x="56" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="56" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="64" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="64" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="72" y="4" fill="currentcolor" style="font-size:1em">v</text><text text-anchor="middle" x="72" y="20" fill="currentcolor" style="font-size:1em">v</text><text text-anchor="middle" x="80" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="80" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="88" y="4" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="88" y="20" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="96" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="96" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="104" y="4" fill="currentcolor" style="font-size:1em">D</text><text text-anchor="middle" x="104" y="20" fill="currentcolor" style="font-size:1em">P</text><text text-anchor="middle" x="112" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="112" y="20" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="120" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="120" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="128" y="20" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="144" y="4" fill="currentcolor" style="font-size:1em">v</text><text text-anchor="middle" x="144" y="20" fill="currentcolor" style="font-size:1em">8</text><text text-anchor="middle" x="152" y="4" fill="currentcolor" style="font-size:1em">a</text><text text-anchor="middle" x="152" y="20" fill="currentcolor" style="font-size:1em">0</text><text text-anchor="middle" x="160" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="168" y="4" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="168" y="20" fill="currentcolor" style="font-size:1em">1</text><text text-anchor="middle" x="176" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="176" y="20" fill="currentcolor" style="font-size:1em">2</text><text text-anchor="middle" x="184" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="184" y="20" fill="currentcolor" style="font-size:1em">7</text><text text-anchor="middle" x="192" y="4" fill="currentcolor" style="font-size:1em">b</text><text text-anchor="middle" x="192" y="20" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="200" y="20" fill="currentcolor" style="font-size:1em">0</text><text text-anchor="middle" x="208" y="4" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="208" y="20" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="216" y="4" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="216" y="20" fill="currentcolor" style="font-size:1em">0</text><text text-anchor="middle" x="224" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="224" y="20" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="232" y="20" fill="currentcolor" style="font-size:1em">1</text><text text-anchor="middle" x="240" y="4" fill="currentcolor" style="font-size:1em">h</text><text text-anchor="middle" x="240" y="20" fill="currentcolor" style="font-size:1em">:</text><text text-anchor="middle" x="248" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="248" y="20" fill="currentcolor" style="font-size:1em">3</text><text text-anchor="middle" x="256" y="4" fill="currentcolor" style="font-size:1em">d</text><text text-anchor="middle" x="256" y="20" fill="currentcolor" style="font-size:1em">3</text><text text-anchor="middle" x="264" y="4" fill="currentcolor" style="font-size:1em">d</text><text text-anchor="middle" x="264" y="20" fill="currentcolor" style="font-size:1em">0</text><text text-anchor="middle" x="272" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="272" y="20" fill="currentcolor" style="font-size:1em">1</text><text text-anchor="middle" x="280" y="4" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="288" y="4" fill="currentcolor" style="font-size:1em">_</text><text text-anchor="middle" x="296" y="4" fill="currentcolor" style="font-size:1em">s</text><text text-anchor="middle" x="304" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="312" y="4" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="320" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="328" y="4" fill="currentcolor" style="font-size:1em">/</text></g></svg></div><p>Make sure the directory is owned by Tors user and private:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo mkdir -p /var/lib/tor/hidden_site
</span></span><span style=display:flex><span>sudo chown -R debian-tor:debian-tor /var/lib/tor/hidden_site
</span></span><span style=display:flex><span>sudo chmod <span style=color:#ae81ff>700</span> /var/lib/tor/hidden_site
</span></span></code></pre></div><p><strong>Important:</strong> use the right systemd unit:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#75715e># validate as the Tor user</span>
</span></span><span style=display:flex><span>sudo -u debian-tor tor -f /etc/tor/torrc --verify-config
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#75715e># (re)start the real service</span>
</span></span><span style=display:flex><span>sudo systemctl enable --now tor@default
</span></span><span style=display:flex><span>sudo systemctl restart tor@default
</span></span></code></pre></div><p>Get the onion address:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo cat /var/lib/tor/hidden_site/hostname
</span></span></code></pre></div><p>Quick check (do remote DNS via SOCKS):</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style=color:#e6db74>&#34;http://</span><span style=color:#66d9ef>$(</span>sudo cat /var/lib/tor/hidden_site/hostname<span style=color:#66d9ef>)</span><span style=color:#e6db74>&#34;</span>
</span></span></code></pre></div><hr><h2 id=2-nginx-for-the-onion-localhost-only>2) Nginx for the onion (localhost-only)<a hidden class=anchor aria-hidden=true href=#2-nginx-for-the-onion-localhost-only>#</a></h2><p>I keep the onion site strictly on localhost: <strong>no HTTPS, no redirects, no HSTS</strong>. Tor already provides e2e encryption and authenticity.</p><p><code>/etc/nginx/sites-available/onion-blog</code>:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-nginx data-lang=nginx><span style=display:flex><span><span style=color:#66d9ef>server</span> {
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> 127.0.0.1:<span style=color:#ae81ff>3301</span> <span style=color:#e6db74>default_server</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>server_name</span> <span style=color:#e6db74>&lt;your-56-char&gt;.onion</span> 127.0.0.1 <span style=color:#e6db74>localhost</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#75715e># keep onion simple; no HSTS/redirects here
</span></span></span><span style=display:flex><span><span style=color:#75715e></span> <span style=color:#f92672>add_header</span> <span style=color:#e6db74>Referrer-Policy</span> <span style=color:#e6db74>no-referrer</span> <span style=color:#e6db74>always</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>add_header</span> <span style=color:#e6db74>X-Content-Type-Options</span> <span style=color:#e6db74>nosniff</span> <span style=color:#e6db74>always</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>add_header</span> <span style=color:#e6db74>X-Frame-Options</span> <span style=color:#e6db74>SAMEORIGIN</span> <span style=color:#e6db74>always</span>;
</span></span><span style=display:flex><span> <span style=color:#75715e># (optional) strict CSP so nothing leaks to clearnet
</span></span></span><span style=display:flex><span><span style=color:#75715e></span> <span style=color:#75715e># add_header Content-Security-Policy &#34;default-src &#39;self&#39;; img-src &#39;self&#39; data:; style-src &#39;self&#39; &#39;unsafe-inline&#39;; script-src &#39;self&#39;&#34; always;
</span></span></span><span style=display:flex><span><span style=color:#75715e></span>
</span></span><span style=display:flex><span> <span style=color:#f92672>root</span> <span style=color:#e6db74>/srv/hugo/mysite/public-onion</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>index</span> <span style=color:#e6db74>index.html</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#f92672>location</span> <span style=color:#e6db74>/</span> { <span style=color:#f92672>try_files</span> $uri $uri/ <span style=color:#e6db74>/index.html</span>; }
</span></span><span style=display:flex><span> <span style=color:#f92672>location</span> ~<span style=color:#e6db74>*</span> <span style=color:#e6db74>\.(css|js|ico|png|jpg|jpeg|gif|svg|webp|txt|xml)</span>$ {
</span></span><span style=display:flex><span> <span style=color:#f92672>access_log</span> <span style=color:#66d9ef>off</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>add_header</span> <span style=color:#e6db74>Cache-Control</span> <span style=color:#e6db74>&#34;public,</span> <span style=color:#e6db74>max-age=31536000,</span> <span style=color:#e6db74>immutable&#34;</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>try_files</span> $uri =<span style=color:#ae81ff>404</span>;
</span></span><span style=display:flex><span> }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><p>Enable/reload:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo ln -sf /etc/nginx/sites-available/onion-blog /etc/nginx/sites-enabled/onion-blog
</span></span><span style=display:flex><span>sudo nginx -t <span style=color:#f92672>&amp;&amp;</span> sudo systemctl reload nginx
</span></span></code></pre></div><blockquote><p>Gotcha I hit: I had <strong>two</strong> server blocks on <code>127.0.0.1:3301</code>, which caused 404s via onion. Make sure only the intended vhost listens there (or mark it <code>default_server</code>). Also, if you ever use a regex in <code>server_name</code>, the syntax is <code>server_name ~* \.onion$</code> (note the space after <code>~*</code>).</p></blockquote><hr><h2 id=3-hugo--papermod-setup-and-version-bumps>3) Hugo + PaperMod setup (and version bumps)<a hidden class=anchor aria-hidden=true href=#3-hugo--papermod-setup-and-version-bumps>#</a></h2><p>PaperMod now requires <strong>Hugo Extended ≥ 0.146.0</strong>. I installed the extended binary from the official tarball to avoid Snaps sandbox limitations (Snap cant read <code>/srv</code> paths by default).</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span><span style=color:#75715e># install Hugo extended (example)</span>
</span></span><span style=display:flex><span>VER<span style=color:#f92672>=</span>0.146.0
</span></span><span style=display:flex><span>cd /tmp
</span></span><span style=display:flex><span>wget https://github.com/gohugoio/hugo/releases/download/v<span style=color:#e6db74>${</span>VER<span style=color:#e6db74>}</span>/hugo_extended_<span style=color:#e6db74>${</span>VER<span style=color:#e6db74>}</span>_Linux-amd64.tar.gz
</span></span><span style=display:flex><span>tar -xzf hugo_extended_<span style=color:#e6db74>${</span>VER<span style=color:#e6db74>}</span>_Linux-amd64.tar.gz
</span></span><span style=display:flex><span>sudo mv hugo /usr/local/bin/hugo
</span></span><span style=display:flex><span>hugo version <span style=color:#75715e># should say &#34;extended&#34; and &gt;= 0.146.0</span>
</span></span></code></pre></div><p>Create the site and theme:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo mkdir -p /srv/hugo <span style=color:#f92672>&amp;&amp;</span> sudo chown -R <span style=color:#e6db74>&#34;</span>$USER<span style=color:#e6db74>&#34;</span>:<span style=color:#e6db74>&#34;</span>$USER<span style=color:#e6db74>&#34;</span> /srv/hugo
</span></span><span style=display:flex><span>cd /srv/hugo
</span></span><span style=display:flex><span>hugo new site mysite
</span></span><span style=display:flex><span>cd mysite
</span></span><span style=display:flex><span>git init
</span></span><span style=display:flex><span>git submodule add https://github.com/adityatelange/hugo-PaperMod themes/PaperMod
</span></span></code></pre></div><p><strong>Config updates:</strong> in newer Hugo, <code>paginate</code> is deprecated → use:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-toml data-lang=toml><span style=display:flex><span><span style=color:#75715e># config/_default/hugo.toml</span>
</span></span><span style=display:flex><span><span style=color:#a6e22e>title</span> = <span style=color:#e6db74>&#34;My Blog&#34;</span>
</span></span><span style=display:flex><span><span style=color:#a6e22e>theme</span> = <span style=color:#e6db74>&#34;PaperMod&#34;</span>
</span></span><span style=display:flex><span><span style=color:#a6e22e>enableRobotsTXT</span> = <span style=color:#66d9ef>true</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>[<span style=color:#a6e22e>pagination</span>]
</span></span><span style=display:flex><span> <span style=color:#a6e22e>pagerSize</span> = <span style=color:#ae81ff>10</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span>[<span style=color:#a6e22e>params</span>]
</span></span><span style=display:flex><span> <span style=color:#a6e22e>defaultTheme</span> = <span style=color:#e6db74>&#34;auto&#34;</span>
</span></span><span style=display:flex><span> <span style=color:#a6e22e>showReadingTime</span> = <span style=color:#66d9ef>true</span>
</span></span><span style=display:flex><span> <span style=color:#a6e22e>showPostNavLinks</span> = <span style=color:#66d9ef>true</span>
</span></span><span style=display:flex><span> <span style=color:#a6e22e>showBreadCrumbs</span> = <span style=color:#66d9ef>true</span>
</span></span><span style=display:flex><span> <span style=color:#a6e22e>showCodeCopyButtons</span> = <span style=color:#66d9ef>true</span>
</span></span></code></pre></div><p>I added per-environment overrides so I can build two outputs with different <code>baseURL</code>s:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-toml data-lang=toml><span style=display:flex><span><span style=color:#75715e># config/clearnet/hugo.toml</span>
</span></span><span style=display:flex><span><span style=color:#a6e22e>baseURL</span> = <span style=color:#e6db74>&#34;https://blog.alipourimjourneys.ir/&#34;</span>
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#75715e># config/onion/hugo.toml</span>
</span></span><span style=display:flex><span><span style=color:#a6e22e>baseURL</span> = <span style=color:#e6db74>&#34;http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/&#34;</span>
</span></span></code></pre></div><hr><h2 id=4-dual-builds-clearnet--onion-and-one-command-deploy>4) Dual builds (clearnet + onion) and one-command deploy<a hidden class=anchor aria-hidden=true href=#4-dual-builds-clearnet--onion-and-one-command-deploy>#</a></h2><p>I publish the same content twice—once for each base URL and docroot:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>cd /srv/hugo/mysite
</span></span><span style=display:flex><span><span style=color:#75715e># clearnet build (served over HTTPS)</span>
</span></span><span style=display:flex><span>hugo --minify --environment clearnet -d public-clearnet
</span></span><span style=display:flex><span><span style=color:#75715e># onion build (served via Tor)</span>
</span></span><span style=display:flex><span>hugo --minify --environment onion -d public-onion
</span></span><span style=display:flex><span>sudo systemctl reload nginx
</span></span></code></pre></div><p>Helper script I use:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo tee /usr/local/bin/build-both &gt;/dev/null <span style=color:#e6db74>&lt;&lt;&#39;EOF&#39;
</span></span></span><span style=display:flex><span><span style=color:#e6db74>#!/usr/bin/env bash
</span></span></span><span style=display:flex><span><span style=color:#e6db74>set -euo pipefail
</span></span></span><span style=display:flex><span><span style=color:#e6db74>cd /srv/hugo/mysite
</span></span></span><span style=display:flex><span><span style=color:#e6db74>hugo --minify --environment clearnet -d public-clearnet
</span></span></span><span style=display:flex><span><span style=color:#e6db74>hugo --minify --environment onion -d public-onion
</span></span></span><span style=display:flex><span><span style=color:#e6db74>sudo systemctl reload nginx
</span></span></span><span style=display:flex><span><span style=color:#e6db74>echo &#34;Deployed both at $(date)&#34;
</span></span></span><span style=display:flex><span><span style=color:#e6db74>EOF</span>
</span></span><span style=display:flex><span>sudo chmod +x /usr/local/bin/build-both
</span></span></code></pre></div><p>While editing, I sometimes auto-rebuild on file save:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo apt install -y entr
</span></span><span style=display:flex><span>cd /srv/hugo/mysite
</span></span><span style=display:flex><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div><hr><h2 id=5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01>5) Clearnet behind Cloudflare + Lets Encrypt (manual DNS-01)<a hidden class=anchor aria-hidden=true href=#5-clearnet-behind-cloudflare--lets-encrypt-manual-dns-01>#</a></h2><p>Cloudflare is set to <strong>Full (strict)</strong>. I issued a public cert for <code>blog.alipourimjourneys.ir</code> using <strong>manual DNS-01</strong> (no API token):</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>sudo snap install --classic certbot
</span></span><span style=display:flex><span>sudo certbot certonly --manual --preferred-challenges dns -d blog.alipourimjourneys.ir --agree-tos -m you@example.com --no-eff-email
</span></span></code></pre></div><p>Certbot tells you to add a TXT record <code>_acme-challenge.blog.alipourimjourneys.ir</code>. Add it in Cloudflare DNS, verify with <code>dig</code>, then continue. Cert ends up at:</p><div class="goat svg-container"><svg font-family="Menlo,Lucida Console,monospace" viewBox="0 0 496 41"><g transform="translate(8,16)"><text text-anchor="middle" x="0" y="4" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="0" y="20" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="8" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="8" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="16" y="4" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="16" y="20" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="24" y="4" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="24" y="20" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="32" y="4" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="32" y="20" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="40" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="40" y="20" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="48" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="48" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="56" y="4" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="56" y="20" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="64" y="4" fill="currentcolor" style="font-size:1em">s</text><text text-anchor="middle" x="64" y="20" fill="currentcolor" style="font-size:1em">s</text><text text-anchor="middle" x="72" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="72" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="80" y="4" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="80" y="20" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="88" y="4" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="88" y="20" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="96" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="96" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="104" y="4" fill="currentcolor" style="font-size:1em">y</text><text text-anchor="middle" x="104" y="20" fill="currentcolor" style="font-size:1em">y</text><text text-anchor="middle" x="112" y="4" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="112" y="20" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="120" y="4" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="120" y="20" fill="currentcolor" style="font-size:1em">t</text><text text-anchor="middle" x="128" y="4" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="128" y="20" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="136" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="136" y="20" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="144" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="144" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="152" y="4" fill="currentcolor" style="font-size:1em">v</text><text text-anchor="middle" x="152" y="20" fill="currentcolor" style="font-size:1em">v</text><text text-anchor="middle" x="160" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="160" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="168" y="4" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="168" y="20" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="176" y="4" fill="currentcolor" style="font-size:1em">b</text><text text-anchor="middle" x="176" y="20" fill="currentcolor" style="font-size:1em">b</text><text text-anchor="middle" x="184" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="184" y="20" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="192" y="4" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="192" y="20" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="200" y="4" fill="currentcolor" style="font-size:1em">g</text><text text-anchor="middle" x="200" y="20" fill="currentcolor" style="font-size:1em">g</text><text text-anchor="middle" x="208" y="4" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="208" y="20" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="216" y="4" fill="currentcolor" style="font-size:1em">a</text><text text-anchor="middle" x="216" y="20" fill="currentcolor" style="font-size:1em">a</text><text text-anchor="middle" x="224" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="224" y="20" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="232" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="232" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="240" y="4" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="240" y="20" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="248" y="4" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="248" y="20" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="256" y="4" fill="currentcolor" style="font-size:1em">u</text><text text-anchor="middle" x="256" y="20" fill="currentcolor" style="font-size:1em">u</text><text text-anchor="middle" x="264" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="264" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="272" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="272" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="280" y="4" fill="currentcolor" style="font-size:1em">m</text><text text-anchor="middle" x="280" y="20" fill="currentcolor" style="font-size:1em">m</text><text text-anchor="middle" x="288" y="4" fill="currentcolor" style="font-size:1em">j</text><text text-anchor="middle" x="288" y="20" fill="currentcolor" style="font-size:1em">j</text><text text-anchor="middle" x="296" y="4" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="296" y="20" fill="currentcolor" style="font-size:1em">o</text><text text-anchor="middle" x="304" y="4" fill="currentcolor" style="font-size:1em">u</text><text text-anchor="middle" x="304" y="20" fill="currentcolor" style="font-size:1em">u</text><text text-anchor="middle" x="312" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="312" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="320" y="4" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="320" y="20" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="328" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="328" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="336" y="4" fill="currentcolor" style="font-size:1em">y</text><text text-anchor="middle" x="336" y="20" fill="currentcolor" style="font-size:1em">y</text><text text-anchor="middle" x="344" y="4" fill="currentcolor" style="font-size:1em">s</text><text text-anchor="middle" x="344" y="20" fill="currentcolor" style="font-size:1em">s</text><text text-anchor="middle" x="352" y="4" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="352" y="20" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="360" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="360" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="368" y="4" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="368" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="376" y="4" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="376" y="20" fill="currentcolor" style="font-size:1em">/</text><text text-anchor="middle" x="384" y="4" fill="currentcolor" style="font-size:1em">f</text><text text-anchor="middle" x="384" y="20" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="392" y="4" fill="currentcolor" style="font-size:1em">u</text><text text-anchor="middle" x="392" y="20" fill="currentcolor" style="font-size:1em">r</text><text text-anchor="middle" x="400" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="400" y="20" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="408" y="4" fill="currentcolor" style="font-size:1em">l</text><text text-anchor="middle" x="408" y="20" fill="currentcolor" style="font-size:1em">v</text><text text-anchor="middle" x="416" y="4" fill="currentcolor" style="font-size:1em">c</text><text text-anchor="middle" x="416" y="20" fill="currentcolor" style="font-size:1em">k</text><text text-anchor="middle" x="424" y="4" fill="currentcolor" style="font-size:1em">h</text><text text-anchor="middle" x="424" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="432" y="4" fill="currentcolor" style="font-size:1em">a</text><text text-anchor="middle" x="432" y="20" fill="currentcolor" style="font-size:1em">y</text><text text-anchor="middle" x="440" y="4" fill="currentcolor" style="font-size:1em">i</text><text text-anchor="middle" x="440" y="20" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="448" y="4" fill="currentcolor" style="font-size:1em">n</text><text text-anchor="middle" x="448" y="20" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="456" y="4" fill="currentcolor" style="font-size:1em">.</text><text text-anchor="middle" x="456" y="20" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="464" y="4" fill="currentcolor" style="font-size:1em">p</text><text text-anchor="middle" x="464" y="20" fill="currentcolor" style="font-size:1em">m</text><text text-anchor="middle" x="472" y="4" fill="currentcolor" style="font-size:1em">e</text><text text-anchor="middle" x="480" y="4" fill="currentcolor" style="font-size:1em">m</text></g></svg></div><p>Clearnet Nginx vhosts:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-nginx data-lang=nginx><span style=display:flex><span><span style=color:#75715e># HTTP → HTTPS redirect (optional; CF usually talks HTTPS to origin anyway)
</span></span></span><span style=display:flex><span><span style=color:#75715e></span><span style=color:#66d9ef>server</span> {
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> <span style=color:#ae81ff>80</span>; <span style=color:#f92672>listen</span> <span style=color:#e6db74>[::]:80</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>server_name</span> <span style=color:#e6db74>blog.alipourimjourneys.ir</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>return</span> <span style=color:#ae81ff>301</span> <span style=color:#e6db74>https://blog.alipourimjourneys.ir</span>$request_uri;
</span></span><span style=display:flex><span>}
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span><span style=color:#75715e># HTTPS origin (behind Cloudflare)
</span></span></span><span style=display:flex><span><span style=color:#75715e></span><span style=color:#66d9ef>server</span> {
</span></span><span style=display:flex><span> <span style=color:#f92672>listen</span> <span style=color:#ae81ff>443</span> <span style=color:#e6db74>ssl</span> <span style=color:#e6db74>http2</span>; <span style=color:#f92672>listen</span> <span style=color:#e6db74>[::]:443</span> <span style=color:#e6db74>ssl</span> <span style=color:#e6db74>http2</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>server_name</span> <span style=color:#e6db74>blog.alipourimjourneys.ir</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#f92672>ssl_certificate</span> <span style=color:#e6db74>/etc/letsencrypt/live/blog.alipourimjourneys.ir/fullchain.pem</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>ssl_certificate_key</span> <span style=color:#e6db74>/etc/letsencrypt/live/blog.alipourimjourneys.ir/privkey.pem</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#75715e># HSTS on clearnet only
</span></span></span><span style=display:flex><span><span style=color:#75715e></span> <span style=color:#f92672>add_header</span> <span style=color:#e6db74>Strict-Transport-Security</span> <span style=color:#e6db74>&#34;max-age=31536000</span>; <span style=color:#f92672>includeSubDomains</span>; <span style=color:#f92672>preload&#34;</span> <span style=color:#e6db74>always</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#75715e># Help Tor Browser discover the onion mirror (safe on clearnet)
</span></span></span><span style=display:flex><span><span style=color:#75715e></span> <span style=color:#f92672>add_header</span> <span style=color:#e6db74>Onion-Location</span> <span style=color:#e6db74>&#34;http://&lt;your-56-char&gt;.onion</span>$request_uri&#34; <span style=color:#e6db74>always</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#f92672>root</span> <span style=color:#e6db74>/srv/hugo/mysite/public-clearnet</span>;
</span></span><span style=display:flex><span> <span style=color:#f92672>index</span> <span style=color:#e6db74>index.html</span>;
</span></span><span style=display:flex><span>
</span></span><span style=display:flex><span> <span style=color:#f92672>location</span> <span style=color:#e6db74>/</span> { <span style=color:#f92672>try_files</span> $uri $uri/ <span style=color:#e6db74>/index.html</span>; }
</span></span><span style=display:flex><span>}
</span></span></code></pre></div><blockquote><p>Note: dont add HSTS or HTTPS redirects to the <strong>onion</strong> vhost. Keep onion pure HTTP on localhost.</p></blockquote><hr><h2 id=6-troubleshooting-i-ran-into-and-fixes>6) Troubleshooting I ran into (and fixes)<a hidden class=anchor aria-hidden=true href=#6-troubleshooting-i-ran-into-and-fixes>#</a></h2><ul><li><strong>Tor unit confusion:</strong> <code>tor.service</code> is a tiny master; the real daemon is <code>tor@default</code>. Use that unit and verify config as <code>debian-tor</code>.</li><li><strong>Permissions:</strong> <code>HiddenServiceDir</code> must be owned by <code>debian-tor</code> and mode <code>700</code>.</li><li><strong>Mapping mismatch:</strong> If <code>HiddenServicePort 80 127.0.0.1:3301</code> is set, visit <code>http://&lt;onion>/</code> (no <code>:3301</code>). If you set <code>HiddenServicePort 3301 127.0.0.1:3301</code>, you must use <code>http://&lt;onion>:3301/</code>.</li><li><strong>Curl & .onion:</strong> modern curl refuses <code>.onion</code> unless you use remote DNS via SOCKS:<div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>curl -I --socks5-hostname 127.0.0.1:9050 <span style=color:#e6db74>&#34;http://&lt;onion&gt;/&#34;</span>
</span></span></code></pre></div></li><li><strong>Two Nginx vhosts on the same port:</strong> I had a duplicate server on <code>127.0.0.1:3301</code> pointing somewhere else, which caused onion 404s. Keep only one (or mark one <code>default_server</code>).</li><li><strong>Regex in <code>server_name</code>:</strong> if you use it, write <code>server_name ~* \.onion$</code> (space after <code>~*</code>). I fixed an <code>invalid variable name</code> error caused by a missing space.</li><li><strong>PaperMod with old Hugo:</strong> upgraded to <strong>extended ≥ 0.146.0</strong>. Also updated <code>paginate</code><code>[pagination].pagerSize</code>.</li><li><strong>Snap confinement:</strong> Snaps <code>hugo</code> couldnt read <code>/srv</code> (<code>.../void: permission denied</code>). Switched to the tarball build in <code>/usr/local/bin</code>.</li></ul><hr><h2 id=7-not-secure-in-tor-browser>7) “Not secure” in Tor Browser?<a hidden class=anchor aria-hidden=true href=#7-not-secure-in-tor-browser>#</a></h2><p>That message can appear because onion uses <strong>HTTP</strong>. Its OK: Tor provides e2e encryption + onion auth. If you enable HTTPS-Only Mode, add an exception for the site. Also ensure the onion build doesnt reference <strong>clearnet</strong> resources (scan the built HTML for <code>http(s)://</code> links that arent your onion).</p><hr><h2 id=8-day-to-day-workflow>8) Day-to-day workflow<a hidden class=anchor aria-hidden=true href=#8-day-to-day-workflow>#</a></h2><ul><li>Create content:<div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>hugo new posts/my-first-post.md <span style=color:#75715e># then set draft: false</span>
</span></span></code></pre></div></li><li>Publish both:<div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>build-both
</span></span></code></pre></div></li><li>(Optional) Auto on save:<div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash><span style=display:flex><span>find content layouts assets static config -type f | entr -r build-both
</span></span></code></pre></div></li><li>(Optional) Git push-to-deploy with a bare repo + post-receive hook that runs <code>build-both</code>.</li></ul><hr><h2 id=final-notes>Final notes<a hidden class=anchor aria-hidden=true href=#final-notes>#</a></h2><ul><li>Clearnet gets <strong>HTTPS + HSTS</strong> and an <code>Onion-Location</code> header.</li><li>Onion gets <strong>no HSTS/redirects</strong>, and all assets are self-hosted to avoid mixed content.</li><li>Serving both worlds from one Hugo repo is easy: <strong>two builds, two vhosts, one workflow</strong>.</li></ul></div><footer class=post-footer><ul class=post-tags><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/tor/>Tor</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/nginx/>Nginx</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/hugo/>Hugo</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/papermod/>Papermod</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/cloudflare/>Cloudflare</a></li><li><a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/tags/letsencrypt/>Letsencrypt</a></li></ul><nav class=paginav><a class=prev href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/minecraft_server/><span class=title>« Prev</span><br><span>Dockerizing my Minecraft Server + Geyser: from 'no space left' to stable releases</span>
</a><a class=next href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/><span class=title>Next »</span><br><span>Stealth Trojan VPN Behind Cloudflare Guide</span></a></nav></footer><div class=comments-switch style="display:flex;gap:.5rem;margin:.5rem 0 1rem"><button id=btn-isso type=button aria-pressed=true>Anonymous (Isso)</button>
<button id=btn-giscus type=button aria-pressed=false>GitHub (Giscus)</button>
<a href=https://github.com/AlipourIm/blog-comments/discussions/categories/comments target=_blank rel=noopener>Open on GitHub ↗</a></div><div id=panel-isso><section id=isso-thread></section><script src=/isso/js/embed.min.js data-isso=/isso data-isso-css=true data-isso-lang=en data-isso-max-comments-nested=5 data-isso-sorting=newest async></script><small class=isso-powered style="display:block;margin:.5rem 0;color:var(--secondary,#888)">Comments powered by <a href=https://isso-comments.de target=_blank rel=noopener>Isso</a></small></div><div id=panel-giscus style=display:none><section id=giscus-thread></section></div><script>(function(){var t=document.getElementById("btn-isso"),n=document.getElementById("btn-giscus"),o=document.getElementById("panel-isso"),i=document.getElementById("panel-giscus"),s=!1;function e(e){var a,r=e==="isso";o.style.display=r?"":"none",i.style.display=r?"none":"",t.setAttribute("aria-pressed",r),n.setAttribute("aria-pressed",!r),!r&&!s&&(a=document.createElement("script"),a.src="https://giscus.app/client.js",a.async=!0,a.crossOrigin="anonymous",a.setAttribute("data-repo","AlipourIm/blog-comments"),a.setAttribute("data-repo-id","R_kgDOQGARyA"),a.setAttribute("data-category","Comments"),a.setAttribute("data-category-id","DIC_kwDOQGARyM4Cw3x-"),a.setAttribute("data-mapping","pathname"),a.setAttribute("data-strict","0"),a.setAttribute("data-reactions-enabled","1"),a.setAttribute("data-emit-metadata","0"),a.setAttribute("data-input-position","bottom"),a.setAttribute("data-theme","preferred_color_scheme"),a.setAttribute("data-lang","en"),document.getElementById("giscus-thread").appendChild(a),s=!0)}t.addEventListener("click",function(){e("isso")}),n.addEventListener("click",function(){e("giscus")}),e("isso")})()</script></article></main><footer class=footer><span>&copy; 2025 <a href=http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/>AlipourIm journeys</a></span> ·
<span>Powered by
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg>
</a><a href=/index.xml rel=alternate type=application/rss+xml title=RSS class=rss-link><svg viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>
<span>RSS</span>
</a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script><script>document.querySelectorAll("pre > code").forEach(e=>{const n=e.parentNode.parentNode,t=document.createElement("button");t.classList.add("copy-code"),t.innerHTML="copy";function s(){t.innerHTML="copied!",setTimeout(()=>{t.innerHTML="copy"},2e3)}t.addEventListener("click",t=>{if("clipboard"in navigator){navigator.clipboard.writeText(e.textContent),s();return}const n=document.createRange();n.selectNodeContents(e);const o=window.getSelection();o.removeAllRanges(),o.addRange(n);try{document.execCommand("copy"),s()}catch{}o.removeRange(n)}),n.classList.contains("highlight")?n.appendChild(t):n.parentNode.firstChild==n||(e.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName=="TABLE"?e.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(t):e.parentNode.appendChild(t))})</script></body></html>