# Guides on AlipourIm journeys

http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/categories/guides/ Recent content in Guides on AlipourIm journeys Hugo -- 0.146.0 en Tue, 09 Sep 2025 11:05:00 +0200

# Stealth Trojan VPN Behind Cloudflare Guide

http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/ Tue, 09 Sep 2025 11:05:00 +0200 http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/vpn/<h2 id="introduction">Introduction</h2> <p>So you want a VPN that <strong>doesn&rsquo;t scream &ldquo;I am a VPN&rdquo;</strong> to every censor and firewall out there?<br> Welcome to the world of <strong>Trojan over WebSocket + TLS behind Cloudflare</strong>.</p> <p>This guide not only shows you how to set it up but also sprinkles in some <strong>debugging magic</strong> so you can figure out why things break (and they <em>will</em> break, trust me).</p> <p>We’ll anonymise domains and secrets, so substitute with your own:</p><h2 id="introduction">Introduction</h2> <p>So you want a VPN that <strong>doesn&rsquo;t scream &ldquo;I am a VPN&rdquo;</strong> to every censor and firewall out there?<br> Welcome to the world of <strong>Trojan over WebSocket + TLS behind Cloudflare</strong>.</p> <p>This guide not only shows you how to set it up but also sprinkles in some <strong>debugging magic</strong> so you can figure out why things break (and they <em>will</em> break, trust me).</p> <p>We’ll anonymise domains and secrets, so substitute with your own:</p> <ul> <li>VPN domain: <code>web.example.com</code></li> <li>Panel domain: <code>panel.example.com</code></li> <li>Secret WS path: <code>/stealth-path_abcd1234</code></li> <li>Password: <code>&lt;PASSWORD&gt;</code></li> </ul> <hr> <h2 id="architecture-at-a-glance">Architecture at a Glance</h2> <p>Think of it as a disguise party:</p> <ul> <li><strong>Trojan</strong> = the shy guest (your VPN protocol)</li> <li><strong>Nginx</strong> = the bouncer checking IDs (reverse proxy)</li> <li><strong>Cloudflare</strong> = the doorman who makes sure nobody sees who&rsquo;s inside (CDN &amp; proxy)</li> <li><strong>Your fake website</strong> = the mask (camouflage page)</li> </ul> <p>Traffic flow:</p> <div class="goat svg-container "> <svg xmlns="http://www.w3.org/2000/svg" font-family="Menlo,Lucida Console,monospace" viewBox="0 0 536 25" > <g transform='translate(8,16)'> <text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>C</text> <text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>i</text> <text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>n</text> <text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>→</text> <text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>C</text> <text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>u</text> <text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>d</text> <text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>f</text> <text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>r</text> <text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>(</text> <text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>3</text> <text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>)</text> <text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>→</text> <text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>N</text> <text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>g</text> <text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>i</text> <text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>n</text> <text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>x</text> <text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>(</text> <text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>3</text> <text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>)</text> <text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>→</text> <text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>T</text> <text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>r</text> <text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>j</text> <text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>n</text> <text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>(</text> <text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>c</text> <text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>h</text> <text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>:</text> <text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>5</text> <text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>3</text> <text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>2</text> <text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>1</text> <text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>)</text> </g> </svg> </div> <hr> <h2 id="step-1-panel-setup-panelexamplecom">Step 1: Panel Setup (<code>panel.example.com</code>)</h2> <ul> <li>Bind the 3x-ui panel to localhost (e.g., <code>127.0.0.1:46309</code>).</li> <li>Choose a funky <strong>web base path</strong> like <code>/panel-bananas_42/</code>.</li> <li>Proxy it through Nginx with HTTPS + Basic Auth.</li> <li>Test it at <code>https://panel.example.com/panel-bananas_42/</code>.</li> </ul> <p><strong>Pro tip:</strong> If you see a blank page → your base path is mismatched or Nginx is eating it. Check logs!</p> <hr> <h2 id="step-2-trojan-inbound">Step 2: Trojan Inbound</h2> <p>In 3x-ui, create a Trojan inbound:</p> <ul> <li>Local port: <code>54321</code></li> <li>Transport: WebSocket</li> <li>Path: <code>/stealth-path_abcd1234</code></li> <li>Security: none</li> <li>Password: <code>&lt;PASSWORD&gt;</code></li> </ul> <hr> <h2 id="step-3-nginx-for-vpn-domain-webexamplecom">Step 3: Nginx for VPN Domain (<code>web.example.com</code>)</h2> <p>Your Nginx is the gatekeeper. Sample config:</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> <span style="color:#ae81ff">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">web.example.com</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/web.example.com/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/web.example.com/privkey.pem</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#75715e"># Fake website at root </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/</span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">root</span> <span style="color:#e6db74">/var/www/html</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></span><span style="display:flex;"><span> <span style="color:#75715e"># Real VPN under secret WS path </span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/stealth-path_abcd1234</span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:54321</span>; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><hr> <h2 id="step-4-firewall-rules">Step 4: Firewall Rules</h2> <p>Lock things down! Only Cloudflare should reach you:</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>ufw allow 22/tcp </span></span><span style="display:flex;"><span>ufw allow 80/tcp </span></span><span style="display:flex;"><span>ufw allow 443/tcp </span></span><span style="display:flex;"><span>ufw deny 54321/tcp </span></span></code></pre></div><hr> <h2 id="step-5-client-config">Step 5: Client Config</h2> <p>Trojan URI:</p> <div class="goat svg-container "> <svg xmlns="http://www.w3.org/2000/svg" font-family="Menlo,Lucida Console,monospace" viewBox="0 0 1160 25" > <g transform='translate(8,16)'> <text text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'>r</text> <text text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'>j</text> <text text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'>n</text> <text text-anchor='middle' x='48' y='4' fill='currentColor' style='font-size:1em'>:</text> <text text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'>/</text> <text text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'>/</text> <text text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'>&lt;</text> <text text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'>P</text> <text text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'>A</text> <text text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'>S</text> <text text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'>S</text> <text text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'>W</text> <text text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'>O</text> <text text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'>R</text> <text text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'>D</text> <text text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'>&gt;</text> <text text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'>@</text> <text text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'>w</text> <text text-anchor='middle' x='168' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'>b</text> <text text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'>.</text> <text text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'>x</text> <text text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'>m</text> <text text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'>p</text> <text text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='248' y='4' fill='currentColor' style='font-size:1em'>.</text> <text text-anchor='middle' x='256' y='4' fill='currentColor' style='font-size:1em'>c</text> <text text-anchor='middle' x='264' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='272' y='4' fill='currentColor' style='font-size:1em'>m</text> <text text-anchor='middle' x='280' y='4' fill='currentColor' style='font-size:1em'>:</text> <text text-anchor='middle' x='288' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='296' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='304' y='4' fill='currentColor' style='font-size:1em'>3</text> <text text-anchor='middle' x='312' y='4' fill='currentColor' style='font-size:1em'>?</text> <text text-anchor='middle' x='320' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='328' y='4' fill='currentColor' style='font-size:1em'>y</text> <text text-anchor='middle' x='336' y='4' fill='currentColor' style='font-size:1em'>p</text> <text text-anchor='middle' x='344' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='352' y='4' fill='currentColor' style='font-size:1em'>=</text> <text text-anchor='middle' x='360' y='4' fill='currentColor' style='font-size:1em'>w</text> <text text-anchor='middle' x='368' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='376' y='4' fill='currentColor' style='font-size:1em'>&amp;</text> <text text-anchor='middle' x='384' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='392' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='400' y='4' fill='currentColor' style='font-size:1em'>c</text> <text text-anchor='middle' x='408' y='4' fill='currentColor' style='font-size:1em'>u</text> <text text-anchor='middle' x='416' y='4' fill='currentColor' style='font-size:1em'>r</text> <text text-anchor='middle' x='424' y='4' fill='currentColor' style='font-size:1em'>i</text> <text text-anchor='middle' x='432' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='440' y='4' fill='currentColor' style='font-size:1em'>y</text> <text text-anchor='middle' x='448' y='4' fill='currentColor' style='font-size:1em'>=</text> <text text-anchor='middle' x='456' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='464' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='472' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='480' y='4' fill='currentColor' style='font-size:1em'>&amp;</text> <text text-anchor='middle' x='488' y='4' fill='currentColor' style='font-size:1em'>h</text> <text text-anchor='middle' x='496' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='504' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='512' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='520' y='4' fill='currentColor' style='font-size:1em'>=</text> <text text-anchor='middle' x='528' y='4' fill='currentColor' style='font-size:1em'>w</text> <text text-anchor='middle' x='536' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='544' y='4' fill='currentColor' style='font-size:1em'>b</text> <text text-anchor='middle' x='552' y='4' fill='currentColor' style='font-size:1em'>.</text> <text text-anchor='middle' x='560' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='568' y='4' fill='currentColor' style='font-size:1em'>x</text> <text text-anchor='middle' x='576' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='584' y='4' fill='currentColor' style='font-size:1em'>m</text> <text text-anchor='middle' x='592' y='4' fill='currentColor' style='font-size:1em'>p</text> <text text-anchor='middle' x='600' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='608' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='616' y='4' fill='currentColor' style='font-size:1em'>.</text> <text text-anchor='middle' x='624' y='4' fill='currentColor' style='font-size:1em'>c</text> <text text-anchor='middle' x='632' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='640' y='4' fill='currentColor' style='font-size:1em'>m</text> <text text-anchor='middle' x='648' y='4' fill='currentColor' style='font-size:1em'>&amp;</text> <text text-anchor='middle' x='656' y='4' fill='currentColor' style='font-size:1em'>p</text> <text text-anchor='middle' x='664' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='672' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='680' y='4' fill='currentColor' style='font-size:1em'>h</text> <text text-anchor='middle' x='688' y='4' fill='currentColor' style='font-size:1em'>=</text> <text text-anchor='middle' x='696' y='4' fill='currentColor' style='font-size:1em'>%</text> <text text-anchor='middle' x='704' y='4' fill='currentColor' style='font-size:1em'>2</text> <text text-anchor='middle' x='712' y='4' fill='currentColor' style='font-size:1em'>F</text> <text text-anchor='middle' x='720' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='728' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='736' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='744' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='752' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='760' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='768' y='4' fill='currentColor' style='font-size:1em'>h</text> <text text-anchor='middle' x='776' y='4' fill='currentColor' style='font-size:1em'>-</text> <text text-anchor='middle' x='784' y='4' fill='currentColor' style='font-size:1em'>p</text> <text text-anchor='middle' x='792' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='800' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='808' y='4' fill='currentColor' style='font-size:1em'>h</text> <text text-anchor='middle' x='816' y='4' fill='currentColor' style='font-size:1em'>_</text> <text text-anchor='middle' x='824' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='832' y='4' fill='currentColor' style='font-size:1em'>b</text> <text text-anchor='middle' x='840' y='4' fill='currentColor' style='font-size:1em'>c</text> <text text-anchor='middle' x='848' y='4' fill='currentColor' style='font-size:1em'>d</text> <text text-anchor='middle' x='856' y='4' fill='currentColor' style='font-size:1em'>1</text> <text text-anchor='middle' x='864' y='4' fill='currentColor' style='font-size:1em'>2</text> <text text-anchor='middle' x='872' y='4' fill='currentColor' style='font-size:1em'>3</text> <text text-anchor='middle' x='880' y='4' fill='currentColor' style='font-size:1em'>4</text> <text text-anchor='middle' x='888' y='4' fill='currentColor' style='font-size:1em'>&amp;</text> <text text-anchor='middle' x='896' y='4' fill='currentColor' style='font-size:1em'>s</text> <text text-anchor='middle' x='904' y='4' fill='currentColor' style='font-size:1em'>n</text> <text text-anchor='middle' x='912' y='4' fill='currentColor' style='font-size:1em'>i</text> <text text-anchor='middle' x='920' y='4' fill='currentColor' style='font-size:1em'>=</text> <text text-anchor='middle' x='928' y='4' fill='currentColor' style='font-size:1em'>w</text> <text text-anchor='middle' x='936' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='944' y='4' fill='currentColor' style='font-size:1em'>b</text> <text text-anchor='middle' x='952' y='4' fill='currentColor' style='font-size:1em'>.</text> <text text-anchor='middle' x='960' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='968' y='4' fill='currentColor' style='font-size:1em'>x</text> <text text-anchor='middle' x='976' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='984' y='4' fill='currentColor' style='font-size:1em'>m</text> <text text-anchor='middle' x='992' y='4' fill='currentColor' style='font-size:1em'>p</text> <text text-anchor='middle' x='1000' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='1008' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='1016' y='4' fill='currentColor' style='font-size:1em'>.</text> <text text-anchor='middle' x='1024' y='4' fill='currentColor' style='font-size:1em'>c</text> <text text-anchor='middle' x='1032' y='4' fill='currentColor' style='font-size:1em'>o</text> <text text-anchor='middle' x='1040' y='4' fill='currentColor' style='font-size:1em'>m</text> <text text-anchor='middle' x='1048' y='4' fill='currentColor' style='font-size:1em'>#</text> <text text-anchor='middle' x='1056' y='4' fill='currentColor' style='font-size:1em'>M</text> <text text-anchor='middle' x='1064' y='4' fill='currentColor' style='font-size:1em'>y</text> <text text-anchor='middle' x='1072' y='4' fill='currentColor' style='font-size:1em'>S</text> <text text-anchor='middle' x='1080' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='1088' y='4' fill='currentColor' style='font-size:1em'>e</text> <text text-anchor='middle' x='1096' y='4' fill='currentColor' style='font-size:1em'>a</text> <text text-anchor='middle' x='1104' y='4' fill='currentColor' style='font-size:1em'>l</text> <text text-anchor='middle' x='1112' y='4' fill='currentColor' style='font-size:1em'>t</text> <text text-anchor='middle' x='1120' y='4' fill='currentColor' style='font-size:1em'>h</text> <text text-anchor='middle' x='1128' y='4' fill='currentColor' style='font-size:1em'>V</text> <text text-anchor='middle' x='1136' y='4' fill='currentColor' style='font-size:1em'>P</text> <text text-anchor='middle' x='1144' y='4' fill='currentColor' style='font-size:1em'>N</text> </g> </svg> </div> <p>iOS clients: Shadowrocket, Stash, FoXray<br> Android clients: v2rayNG, Clash Meta, NekoBox</p> <hr> <h2 id="debugging-time-aka-why-the-heck-doesnt-it-work">Debugging Time (a.k.a. “Why the heck doesn’t it work?!”)</h2> <h3 id="symptom-520-unknown-error-cloudflare">Symptom: <strong>520 Unknown Error (Cloudflare)</strong></h3> <ul> <li>Likely cause: Nginx couldn’t talk to Trojan.</li> <li>Check Nginx error log: <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>tail -n <span style="color:#ae81ff">50</span> /var/log/nginx/error.log </span></span></code></pre></div></li> <li>If you see <code>upstream sent no valid HTTP/1.0 header</code> → you’re mixing HTTPS/HTTP between Nginx and Trojan.</li> </ul> <hr> <h3 id="symptom-526-invalid-ssl-certificate">Symptom: <strong>526 Invalid SSL certificate</strong></h3> <ul> <li>Cloudflare → Nginx cert mismatch.</li> <li>Run: <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>openssl s_client -connect web.example.com:443 -servername web.example.com -showcerts </span></span></code></pre></div></li> <li>Make sure CN = <code>web.example.com</code>. If not, fix your cert.</li> </ul> <hr> <h3 id="symptom-blank-page-on-panel">Symptom: <strong>Blank page on panel</strong></h3> <ul> <li>Check if the base path matches exactly (case-sensitive, slash-sensitive).</li> <li>Watch for sneaky trailing spaces!</li> <li>Test locally: <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 -s -D- http://127.0.0.1:46309/panel-bananas_42/ </span></span></code></pre></div></li> </ul> <hr> <h3 id="symptom-client-wont-connect">Symptom: <strong>Client won’t connect</strong></h3> <ul> <li>Run a WebSocket test: <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 -k -H <span style="color:#e6db74">&#34;Connection: Upgrade&#34;</span> -H <span style="color:#e6db74">&#34;Upgrade: websocket&#34;</span> https://web.example.com/stealth-path_abcd1234 </span></span></code></pre></div>Expect <code>101 Switching Protocols</code>. If not, check Nginx config.</li> </ul> <hr> <h3 id="symptom-censor-still-blocks-you">Symptom: <strong>Censor still blocks you</strong></h3> <ul> <li>Did you expose another service (like SSH, Matrix, or Minecraft) on the same IP?<br> → They’ll find your origin IP. Use a second VPS or lock those ports down.</li> <li>Did you use an obvious path like <code>/ws</code>?<br> → Use a random-looking one like <code>/cdn-assets-329df/</code>.</li> </ul> <hr> <h2 id="pro-tips--fun-tricks">Pro Tips &amp; Fun Tricks</h2> <ul> <li>Serve a fake blog or portfolio at root so your domain looks legit.</li> <li>Rotate WebSocket paths occasionally.</li> <li>Use Cloudflare <strong>Page Rules</strong> to disable Rocket Loader &amp; Minify for your VPN domain.</li> <li>Make friends with your Nginx error.log — it will roast you but it tells the truth.</li> </ul> <hr> <h2 id="conclusion">Conclusion</h2> <p>By putting Trojan behind Cloudflare, you’ve given your VPN a shiny new disguise:</p> <ul> <li>Looks like normal HTTPS.</li> <li>Hides your origin IP.</li> <li>Forces censors into a tough choice: block Cloudflare (and half the web) or let you pass.</li> </ul> <p>Congrats — you’ve built a VPN with both <strong>style</strong> and <strong>stealth</strong>. 🥷</p>

# Self-Hosting HedgeDoc with Docker + Nginx + Let's Encrypt

http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/ Fri, 29 Aug 2025 00:00:00 +0000 http://fjthpp2h3mj2rup25r3psmqamutnkbvxbpltlohdthw6fscgo3t6bpad.onion/posts/hedge_doc/<p>HedgeDoc is an open-source collaborative Markdown editor. Think <em>Google Docs for Markdown</em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.</p> <hr> <h2 id="prerequisites">Prerequisites</h2> <ul> <li>A Linux server with <strong>Docker</strong> and <strong>Docker Compose</strong></li> <li>A domain name pointing to your server (e.g. <code>notes.alipourimjourneys.ir</code>)</li> <li><strong>Nginx</strong> installed for reverse proxying</li> <li><strong>Certbot</strong> for Let’s Encrypt certificates</li> </ul> <hr> <h2 id="1-create-the-project-directory">1. Create the project directory</h2> <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>mkdir ~/hedgedoc <span style="color:#f92672">&amp;&amp;</span> cd ~/hedgedoc</span></span></code></pre></div> <hr> <h2 id="2-create-env">2. Create <code>.env</code></h2> <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-env" data-lang="env"><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>ChangeThisStrongPassword </span></span><span style="display:flex;"><span>HD_DOMAIN<span style="color:#f92672">=</span>notes.alipourimjourneys.ir</span></span></code></pre></div> <p>Generate a strong password with:</p><p>HedgeDoc is an open-source collaborative Markdown editor. Think <em>Google Docs for Markdown</em>: multiple people can edit the same note in real-time, with support for diagrams, math, polls, and slide decks. In this post we’ll walk through setting up your own instance on a server, secured with HTTPS.</p> <hr> <h2 id="prerequisites">Prerequisites</h2> <ul> <li>A Linux server with <strong>Docker</strong> and <strong>Docker Compose</strong></li> <li>A domain name pointing to your server (e.g. <code>notes.alipourimjourneys.ir</code>)</li> <li><strong>Nginx</strong> installed for reverse proxying</li> <li><strong>Certbot</strong> for Let’s Encrypt certificates</li> </ul> <hr> <h2 id="1-create-the-project-directory">1. Create the project directory</h2> <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>mkdir ~/hedgedoc <span style="color:#f92672">&amp;&amp;</span> cd ~/hedgedoc</span></span></code></pre></div> <hr> <h2 id="2-create-env">2. Create <code>.env</code></h2> <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-env" data-lang="env"><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>ChangeThisStrongPassword </span></span><span style="display:flex;"><span>HD_DOMAIN<span style="color:#f92672">=</span>notes.alipourimjourneys.ir</span></span></code></pre></div> <p>Generate a strong password with:</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>openssl rand -base64 <span style="color:#ae81ff">32</span></span></span></code></pre></div> <hr> <h2 id="3-create-docker-composeyml">3. Create <code>docker-compose.yml</code></h2> <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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">version</span>: <span style="color:#e6db74">&#34;3.9&#34;</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#f92672">services</span>: </span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>: </span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">postgres:16</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>: </span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">hedgedoc</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">${POSTGRES_PASSWORD}</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">hedgedoc</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>: </span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db:/var/lib/postgresql/data</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">hedgedoc</span>: </span></span><span style="display:flex;"><span> <span style="color:#f92672">image</span>: <span style="color:#ae81ff">quay.io/hedgedoc/hedgedoc:1.10.2</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">depends_on</span>: </span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">db</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>: </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DB_URL</span>: <span style="color:#ae81ff">postgres://hedgedoc:${POSTGRES_PASSWORD}@db:5432/hedgedoc</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_DOMAIN</span>: <span style="color:#ae81ff">${HD_DOMAIN}</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PROTOCOL_USESSL</span>: <span style="color:#e6db74">&#34;true&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_URL_ADDPORT</span>: <span style="color:#e6db74">&#34;false&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_PORT</span>: <span style="color:#e6db74">&#34;3000&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_EMAIL</span>: <span style="color:#e6db74">&#34;true&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">CMD_ALLOW_EMAIL_REGISTER</span>: <span style="color:#e6db74">&#34;false&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">volumes</span>: </span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">uploads:/hedgedoc/public/uploads</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">ports</span>: </span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;127.0.0.1:3000:3000&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#f92672">volumes</span>: </span></span><span style="display:flex;"><span> <span style="color:#f92672">db</span>: </span></span><span style="display:flex;"><span> <span style="color:#ae81ff">uploads:</span></span></span></code></pre></div> <p>Bring it up:</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>docker compose up -d</span></span></code></pre></div> <hr> <h2 id="4-get-a-lets-encrypt-certificate">4. Get a Let’s Encrypt certificate</h2> <p>Request a cert with a <strong>DNS challenge</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 certbot certonly --manual --preferred-challenges dns -d notes.alipourimjourneys.ir -m you@example.com --agree-tos --no-eff-email</span></span></code></pre></div> <p>Add the TXT record certbot asks for, wait for DNS to propagate, then continue.<br> Certificates will be in:</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>/etc/letsencrypt/live/notes.alipourimjourneys.ir/</span></span></code></pre></div> <hr> <h2 id="5-configure-nginx">5. Configure Nginx</h2> <p>Create <code>/etc/nginx/sites-available/notes.alipourimjourneys.ir</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">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>; </span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#e6db74">[::]:80</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://</span>$host$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:#66d9ef">server</span> { </span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">notes.alipourimjourneys.ir</span>; </span></span><span style="display:flex;"><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></span><span style="display:flex;"><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></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/notes.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/notes.alipourimjourneys.ir/privkey.pem</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></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:3000</span>; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> <span style="color:#e6db74">https</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade; </span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">&#34;upgrade&#34;</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>}</span></span></code></pre></div> <p>Enable the config and reload Nginx:</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 -s /etc/nginx/sites-available/notes.alipourimjourneys.ir /etc/nginx/sites-enabled/ </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> <hr> <h2 id="6-create-users">6. Create users</h2> <p>Because we disabled self-registration, create accounts manually:</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>docker compose exec hedgedoc ./bin/manage_users --add alice@example.com</span></span></code></pre></div> <p>You’ll be prompted for a password.<br> To reset a password later:</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>docker compose exec hedgedoc ./bin/manage_users --reset alice@example.com</span></span></code></pre></div> <hr> <h2 id="7-done">7. Done!</h2> <p>Visit <a href="https://notes.alipourimjourneys.ir">https://notes.alipourimjourneys.ir</a> and log in with the user you created. You now have your own collaborative Markdown editor 🎉</p> <hr> <p><strong>Extras:</strong></p> <ul> <li>Backups: dump the PostgreSQL database and save the <code>uploads</code> volume.</li> <li>Upgrades: <code>docker pull quay.io/hedgedoc/hedgedoc:latest &amp;&amp; docker compose up -d</code>.</li> <li>Integrations: HedgeDoc supports S3/MinIO image storage, GitHub/GitLab/Google login, and more.</li> </ul>