<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Certificates on Anigeek</title><link>https://blog.anigeek.com/tags/certificates/</link><description>Recent content in Certificates on Anigeek</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 13 Jun 2025 19:45:00 -0700</lastBuildDate><atom:link href="https://blog.anigeek.com/tags/certificates/index.xml" rel="self" type="application/rss+xml"/><item><title>Setting Up Joplin Server with Caddy and Local HTTPS (tls internal)</title><link>https://blog.anigeek.com/posts/joplin-caddy/</link><pubDate>Fri, 13 Jun 2025 19:45:00 -0700</pubDate><guid>https://blog.anigeek.com/posts/joplin-caddy/</guid><description>How to get Joplin Server working with Caddy&amp;rsquo;s internal TLS without going completely insane</description><content:encoded><![CDATA[<blockquote>
<p><strong>DISCLAIMER: Written by ChatGPT because I&rsquo;m just too tired.</strong> But everything in here is based on what I actually did to make it finally work, and I’m sharing this in the hopes that it saves you some of the frustration I went through.</p></blockquote>
<h2 id="introduction">Introduction</h2>
<p>So you’re self-hosting <a href="https://joplinapp.org/help/apps/#joplin-server">Joplin Server</a> behind a <a href="https://caddyserver.com/">Caddy</a> reverse proxy and thought “Hey, I’ll just use <code>tls internal</code>, and it’ll all be fine.”</p>
<p>Spoiler: it won’t. Not right away, anyway.</p>
<p>This guide walks through exactly how to get Joplin Desktop to stop throwing cert errors (<code>UNABLE_TO_GET_ISSUER_CERT_LOCALLY</code>) and finally sync with your Joplin Server instance over HTTPS, using Caddy’s <strong><code>tls internal</code></strong> feature.</p>
<hr>
<h2 id="my-setup">My Setup</h2>
<ul>
<li><strong>OS:</strong> Linux (Proxmox LXC running Docker)</li>
<li><strong>Reverse Proxy:</strong> Caddy with <code>tls internal</code></li>
<li><strong>Joplin Server:</strong> Docker container (<code>joplin/server:latest</code>)</li>
<li><strong>Joplin Desktop:</strong> Windows client</li>
<li><strong>Domain:</strong> <code>joplin.YOUR_DOMAIN.com</code> (local-only via internal DNS)</li>
</ul>
<hr>
<h2 id="the-problem">The Problem</h2>
<p>Joplin Desktop <strong>does not trust</strong> certificates generated by Caddy’s internal CA out of the box. Even if:</p>
<ul>
<li>You’ve installed the root cert into Windows</li>
<li>You’ve set the <code>NODE_EXTRA_CA_CERTS</code> environment variable</li>
<li>You’ve tried <code>--ignore-certificate-errors</code></li>
<li>You’ve tested with <code>curl</code> or <code>openssl</code> and everything seems fine</li>
</ul>
<p>&hellip;Joplin will still throw:</p>
<pre tabindex="0"><code>UNABLE_TO_GET_ISSUER_CERT_LOCALLY
</code></pre><p>Why? Because the Joplin client does <strong>not</strong> use your system’s CA trust store for internal requests.</p>
<hr>
<h2 id="the-solution">The Solution</h2>
<p>You have to <strong>explicitly point the Joplin Desktop app to the root certificate</strong> under:</p>
<blockquote>
<p><code>Tools → Options → Synchronization → Show Advanced Settings → Custom TLS certificates</code></p></blockquote>
<p>Yes, this setting is collapsed by default. Yes, it will drive you mad. Yes, it’s the only thing that works.</p>
<hr>
<h2 id="step-by-step-guide">Step-by-Step Guide</h2>
<h3 id="1-use-tls-internal-in-your-caddyfile">1. Use <code>tls internal</code> in your Caddyfile</h3>
<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-caddyfile" data-lang="caddyfile"><span style="display:flex;"><span>joplin.YOUR_DOMAIN.com {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">reverse_proxy</span> <span style="color:#e6db74">YOUR_IP_ADDRESS:YOUR_PORT</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">encode</span> <span style="color:#e6db74">gzip</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">tls</span> <span style="color:#66d9ef">internal</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">log</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">output</span> <span style="color:#e6db74">file</span> <span style="color:#e6db74">/var/log/caddy/notes.log</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="2-find-the-caddy-root-certificate">2. Find the Caddy root certificate</h3>
<p>The Caddy root cert lives here:</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>/var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt
</span></span></code></pre></div><h3 id="3-copy-the-root-certificate-to-your-windows-machine">3. Copy the root certificate to your Windows machine</h3>
<p>Save it somewhere easy to find, like:</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-text" data-lang="text"><span style="display:flex;"><span>C:\certs\caddy.crt
</span></span></code></pre></div><p>(If you’re using Notepad++ or similar, make sure line endings are preserved and the file isn’t mangled.)</p>
<h3 id="4-configure-joplin-desktop-to-trust-the-cert">4. Configure Joplin Desktop to trust the cert</h3>
<ol>
<li>Open Joplin Desktop</li>
<li>Go to <strong>Tools → Options → Synchronization</strong></li>
<li>Choose <strong>Joplin Server (Beta)</strong></li>
<li>Enter your server URL and credentials</li>
<li>Click <strong>Show Advanced Settings</strong></li>
<li>Set <code>Custom TLS certificates</code> to:</li>
</ol>
<pre tabindex="0"><code>C:\certs\caddy.crt
</code></pre><ol start="7">
<li>Click <strong>Check synchronization configuration</strong></li>
</ol>
<p>If everything is working, you should get:</p>
<pre tabindex="0"><code>Success! Synchronization configuration appears to be correct.
</code></pre><h3 id="5-profit-">5. Profit 🎉</h3>
<p>Now you can sync across devices without needing to use <code>http://</code>, disable TLS, or punch holes in security you actually want.</p>
<hr>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3 id="q-i-already-installed-the-cert-in-windows-why-didnt-it-work">Q: I already installed the cert in Windows. Why didn’t it work?</h3>
<p>A: Joplin doesn’t use the Windows CA store for API calls. It only affects the embedded Chromium engine (UI stuff).</p>
<h3 id="q-what-about-node_extra_ca_certs">Q: What about <code>NODE_EXTRA_CA_CERTS</code>?</h3>
<p>A: Joplin uses Electron/Node in a way that bypasses that setting for its sync requests.</p>
<h3 id="q-why-didnt-you-mention-this-earlier">Q: Why didn’t you mention this earlier?</h3>
<p>A: Because I didn’t know. Now I do, and so do you.</p>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>Don’t trust the default. Don’t assume the CA store is enough. Don’t assume that just because <code>curl</code> says it’s good, Joplin will agree.</p>
<p>And above all, don’t skip over the hidden “Advanced Settings” button. It’s hiding the only thing that matters.</p>
<p>Stay sane out there.</p>
<hr>
<p><em>If you got here because of a desperate late-night search — welcome. You’re not alone. Hopefully this helped.</em></p>
]]></content:encoded></item></channel></rss>