<?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 – Serverküche</title><link>https://serverkueche.de/en/tags/certificates/</link><description>Certificates – Neueste Beiträge von Serverküche</description><generator>Hugo</generator><language>en-US</language><managingEditor>feedback@serverkueche.de (Serverküche)</managingEditor><webMaster>feedback@serverkueche.de (Serverküche)</webMaster><copyright>2026 Serverküche</copyright><lastBuildDate>Tue, 18 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/en/tags/certificates/index.xml" rel="self" type="application/rss+xml"/><item><title>How HTTPS actually works (and what Traefik does for you)</title><link>https://serverkueche.de/en/tutorials/how-https-works/</link><pubDate>Tue, 18 Aug 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/en/tutorials/how-https-works/</guid><description>Certificate, chain of trust, Let's Encrypt, handshake: this foundational guide explains HTTPS with real commands – and what Traefik automates.</description><content:encoded><![CDATA[<p>The padlock icon in your browser is the most used and least understood security feature of the web. In this concept guide you take a look behind it – with real commands instead of dry theory. Afterwards you&rsquo;ll read certificate errors like a list of ingredients.</p>
<h2 id="what-are-we-building">What are we building?</h2>
<p>No new service this time, but understanding: by the end you know what a TLS certificate is, why your browser trusts it, how Let&rsquo;s Encrypt proves for free that a domain belongs to you, and what happens during the TLS handshake. You verify every building block yourself with <code>openssl</code> and <code>curl</code> – the examples were run with <strong>OpenSSL 3.5</strong> on Debian 13 against the real <code>serverkueche.de</code> and work against any HTTPS website. This is exactly the knowledge <a href="/en/tutorials/traefik-reverse-proxy/">Traefik</a> turns into automation for you – and when something jams there, you&rsquo;ll know <em>where</em> to look.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An understanding of how a <a href="/en/tutorials/connect-domain-to-server/">domain points to your server</a> (A record, <code>dig</code>)</li>
<li>Any Linux terminal – your server or your laptop; you only need standard tools (<code>openssl</code>, <code>curl</code>, <code>dig</code> from the <code>dnsutils</code> package)</li>
<li>No running web server required – we inspect existing websites</li>
</ul>
<div class="not-prose my-6 overflow-hidden rounded-xl border border-paprika-200 bg-paprika-50 dark:border-paprika-800 dark:bg-paprika-900/20"
     data-track-content data-content-name="Affiliate-Box · /en/tutorials/how-https-works/" data-content-piece="VPS 1000 G12">
  <div class="flex items-center justify-between border-b border-paprika-200 bg-paprika-100 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-paprika-700 dark:border-paprika-800 dark:bg-paprika-900/40 dark:text-paprika-300">
    <span>🍳 Recommendation</span>
    <span title="Links marked with * are affiliate links.">Ad</span>
  </div>
  <div class="flex flex-col gap-4 p-4 sm:flex-row sm:items-center sm:justify-between">
    <div>
      <p class="text-lg font-bold text-slate-900 dark:text-white">VPS 1000 G12</p>
      <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">4 vCores · 8 GB RAM · 256 GB NVMe</p>
      <p class="mt-1 text-sm font-semibold text-paprika-700 dark:text-paprika-400">from €10.36/month</p>
      <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">Any machine will do for following along – a small VPS for your own HTTPS setup later.</p>
    </div>
    <a href="https://www.netcup.com/en/server/vps/vps-1000-g12-iv-12m?ref=44083" rel="sponsored noopener" target="_blank"
   data-track-event="Affiliate|netcup: Affiliate-Box|VPS 1000 G12 · {page}"
   class="inline-flex shrink-0 items-center justify-center rounded-lg bg-paprika-600 px-5 py-2.5 font-semibold text-white transition-colors hover:bg-paprika-700">
  Go to netcup →
</a>

  </div><div class="px-4 pb-4"><p class="not-prose my-3 flex flex-wrap items-center gap-x-2 gap-y-1 rounded-lg border border-herb-500/40 bg-herb-50 px-3 py-2 text-sm text-slate-700 dark:bg-herb-900/20 dark:text-slate-200">
  <span>💶 <strong>5 € voucher</strong> for new netcup customers:</span><code data-track-voucher="36nc17844976032"
        class="rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 dark:bg-slate-800 dark:text-herb-400">36nc17844976032</code>
  <span class="text-xs text-slate-500 dark:text-slate-400">(new customers only, no domains)</span>
</p></div>
</div>

<h2 id="step-by-step">Step by step</h2>
<h3 id="step-1-the-two-problems-tls-solves">Step 1: The two problems TLS solves</h3>
<p>HTTPS is plain HTTP, transported through a TLS tunnel (Transport Layer Security). The tunnel solves two problems at once:</p>
<ol>
<li><strong>Confidentiality:</strong> Nobody between browser and server can read along or tamper unnoticed – not the café Wi-Fi, not your ISP.</li>
<li><strong>Authenticity:</strong> You are really talking to the server behind <code>YOUR_DOMAIN</code> – not to an attacker who slipped in between.</li>
</ol>
<p>Point 2 requires an ID card: the <strong>certificate</strong>.</p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-sky-300 bg-sky-50 dark:border-sky-800 dark:bg-sky-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">ℹ️</span>The padlock does not mean “harmless”
  </p>
  <div class="prose-kitchen text-sm">The padlock only confirms an encrypted connection to the domain shown. A phishing site gets a valid certificate just as easily – TLS certifies the <em>pipe</em>, not the <em>content</em>.</div>
</div>
<h3 id="step-2-inspecting-a-real-certificate">Step 2: Inspecting a real certificate</h3>
<p><code>openssl s_client</code> opens a TLS connection and shows what the server presents. Replace <code>YOUR_DOMAIN</code> with any HTTPS domain – here the command runs against <code>serverkueche.de</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="p">|</span> openssl s_client -connect YOUR_DOMAIN:443 -servername YOUR_DOMAIN 2&gt;/dev/null <span class="p">|</span> openssl x509 -noout -subject -issuer -dates</span></span></code></pre></div>
</div>
<p>The <code>echo |</code> closes the connection immediately, the second <code>openssl</code> call translates the presented certificate into plain text:</p>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">subject=CN=serverkueche.de
</span></span><span class="line"><span class="cl">issuer=C=US, O=Let&#39;s Encrypt, CN=YR1
</span></span><span class="line"><span class="cl">notBefore=Jul 16 11:33:26 2026 GMT
</span></span><span class="line"><span class="cl">notAfter=Oct 14 11:33:25 2026 GMT</span></span></code></pre></div>
</div>
<p>These fields are worth being able to read:</p>
<ul>
<li><strong>subject</strong> – who the ID card is for. Which hostnames exactly are covered is listed in the <em>Subject Alternative Name</em> field (here: <code>serverkueche.de</code> and <code>www.serverkueche.de</code> – a subdomain like <code>blog.serverkueche.de</code> would <strong>not</strong> be covered and would need its own certificate).</li>
<li><strong>issuer</strong> – who issued it: the certificate authority (CA), here Let&rsquo;s Encrypt with its intermediate certificate <code>YR1</code>.</li>
<li><strong>notBefore/notAfter</strong> – the validity. Do the math: July 16 to October 14 is <strong>90 days</strong>, the standard lifetime of Let&rsquo;s Encrypt certificates. That&rsquo;s why automatic renewal is not a convenience feature but mandatory.</li>
</ul>
<h3 id="step-3-the-chain-of-trust--why-your-browser-believes-the-id-card">Step 3: The chain of trust – why your browser believes the ID card</h3>
<p>How does your browser know <code>YR1</code> isn&rsquo;t a made-up name? Through a chain. Show all certificates of the connection:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="p">|</span> openssl s_client -connect YOUR_DOMAIN:443 -servername YOUR_DOMAIN 2&gt;/dev/null <span class="p">|</span> grep -E <span class="s2">&#34;^ *[0-9] s:|^ *i:&#34;</span></span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"> 0 s:CN=serverkueche.de
</span></span><span class="line"><span class="cl">   i:C=US, O=Let&#39;s Encrypt, CN=YR1
</span></span><span class="line"><span class="cl"> 1 s:C=US, O=Let&#39;s Encrypt, CN=YR1
</span></span><span class="line"><span class="cl">   i:C=US, O=ISRG, CN=Root YR
</span></span><span class="line"><span class="cl"> 2 s:C=US, O=ISRG, CN=Root YR
</span></span><span class="line"><span class="cl">   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1</span></span></code></pre></div>
</div>
<p>Read it top to bottom (<code>s:</code> = subject, <code>i:</code> = issued by): the domain certificate (0) was signed by the intermediate <code>YR1</code> (1), which in turn was signed by a <strong>root certificate</strong> of ISRG, the organisation behind Let&rsquo;s Encrypt (2). Root certificates are the trust anchor: they ship embedded in your operating system and browser (the “root store”, on Debian the <code>ca-certificates</code> package). If the browser can build an unbroken chain of signatures from the server certificate to a root in its store, the connection counts as trustworthy – <code>openssl</code> reports that in the <code>s_client</code> output as:</p>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Verify return code: 0 (ok)</span></span></code></pre></div>
</div>
<p>Anything other than <code>0 (ok)</code> is one of the failure cases in “When things go wrong”.</p>
<h3 id="step-4-how-lets-encrypt-verifies-the-domain-is-yours">Step 4: How Let&rsquo;s Encrypt verifies the domain is yours</h3>
<p>A CA may only issue a certificate if you prove control over the domain. This runs through the <strong>ACME protocol</strong>, fully automated, in two flavours:</p>
<ul>
<li><strong>HTTP-01:</strong> The CA says “place file X at <code>http://YOUR_DOMAIN/.well-known/acme-challenge/X</code>”. Only whoever controls the server behind the A record can do that. That&rsquo;s why <strong>port 80 must be reachable from the internet</strong> – and why the <code>dig</code> check in the <a href="/en/tutorials/connect-domain-to-server/">domain tutorial</a> comes <em>before</em> the first certificate: if the A record points nowhere, the CA never finds the file.</li>
<li><strong>DNS-01:</strong> Instead of a file you set a TXT record <code>_acme-challenge.YOUR_DOMAIN</code>. This needs API access to your DNS zone, but works without an open port – and is the only way to get wildcard certificates (<code>*.YOUR_DOMAIN</code>).</li>
</ul>
<p>Once your server passes the challenge, Let&rsquo;s Encrypt signs your certificate with <code>YR1</code> – and the chain from step 3 is complete.</p>
<h3 id="step-5-the-handshake--what-happens-on-every-connection">Step 5: The handshake – what happens on every connection</h3>
<p>The certificate proves identity; encryption uses something else. During the <strong>TLS handshake</strong>, browser and server agree on a shared session key within a few milliseconds. <code>curl</code> shows you the result:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -sIv https://YOUR_DOMAIN/ 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep -E <span class="s2">&#34;SSL connection|HTTP/&#34;</span></span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / x25519 / RSASSA-PSS
</span></span><span class="line"><span class="cl">* using HTTP/2
</span></span><span class="line"><span class="cl">HTTP/2 200</span></span></code></pre></div>
</div>
<p>That one line summarises the whole handshake: protocol <strong>TLS 1.3</strong> (the current standard), key exchange over the <code>x25519</code> curve – this is where the session key is created without ever crossing the wire –, then <strong>AES-128-GCM</strong> encrypts the actual HTTP traffic, and <code>RSASSA-PSS</code> is the server&rsquo;s signature made with the private key belonging to its certificate. That private key is the real secret: whoever holds it can impersonate your domain.</p>
<h3 id="step-6-what-traefik-takes-off-your-plate">Step 6: What Traefik takes off your plate</h3>
<p>Now you can put into context everything <a href="/en/tutorials/traefik-reverse-proxy/">Traefik</a> handles in the background:</p>
<ul>
<li><strong>ACME client:</strong> ordering the certificate, answering the HTTP-01 challenge on port 80, fetching the chain – automatically the first time a new app starts.</li>
<li><strong>Storage:</strong> certificates including private keys end up in <code>acme.json</code> – which is why Traefik insists on strict file permissions there (<code>chmod 600</code>).</li>
<li><strong>Renewal:</strong> around 30 days before expiry, Traefik fetches a fresh certificate without you lifting a finger – you never notice the 90-day lifetime.</li>
<li><strong>Serving:</strong> per request, Traefik picks the matching certificate based on the requested hostname (SNI) and redirects HTTP to HTTPS.</li>
</ul>
<p>Without this automation, HTTPS would be monthly manual labour – expired certificates are one of the most common self-inflicted outages there is.</p>
<h2 id="when-things-go-wrong">When things go wrong</h2>
<div class="troubleshoot not-prose">
<p><strong>Browser shows <code>NET::ERR_CERT_AUTHORITY_INVALID</code>, <code>curl</code> says <code>self-signed certificate</code>.</strong> The server is serving Traefik&rsquo;s built-in fallback certificate (<code>TRAEFIK DEFAULT CERT</code>) because issuance failed. Almost always: the A/AAAA record points to the wrong IP (check with <code>dig YOUR_DOMAIN</code>) or port 80 is closed <a href="/en/tutorials/firewall-ufw-setup/">in the firewall</a> – the HTTP-01 challenge never arrives. The exact cause is in the Traefik logs: <code>docker compose logs traefik | grep -i acme</code>.</p>
<p><strong>The <code>issuer</code> says <code>(STAGING) Ersatz Emmer YR2</code>.</strong> Let&rsquo;s Encrypt&rsquo;s test CA is still active – browsers distrust it on purpose. Remove the staging line from the Traefik configuration, empty <code>acme.json</code>, restart the container (<a href="/en/tutorials/traefik-reverse-proxy/">details in the Traefik tutorial</a>).</p>
<p><strong><code>certificate has expired</code>.</strong> Automatic renewal has been failing for at least 30 days – same list of causes as the first error, just long unnoticed. If the message appears <em>on one device only</em>: check that device&rsquo;s system clock, TLS is time-sensitive.</p>
<p><strong>Let&rsquo;s Encrypt reports <code>too many certificates already issued</code>.</strong> Rate limit hit, usually from trial-and-error loops against the production CA. Switch to the staging CA until your setup works – and back up <code>acme.json</code> instead of re-issuing certificates.</p>
<p><strong><code>unable to get local issuer certificate</code> on an old client.</strong> The system is missing the root certificate from step 3 – its root store is outdated. On Debian: <code>apt update &amp;&amp; apt install ca-certificates</code>.</p>

</div>

<h2 id="maintenance--backups">Maintenance &amp; backups</h2>
<ul>
<li><strong>With automated issuance: nothing monthly.</strong> That is precisely the point of the Traefik automation. What you do instead is <strong>monitor</strong> expiry – <a href="/en/tutorials/uptime-kuma-monitoring/">Uptime Kuma</a> checks the certificate as part of HTTPS monitors and warns well before the expiry date. That way a renewal that has been failing for weeks gets noticed <em>before</em> visitors see error pages.</li>
<li><strong><code>acme.json</code> belongs in your backup</strong> – it contains private keys, so back it up encrypted only, e.g. <a href="/en/tutorials/restic-backups/">with Restic</a>. After rebuilding a server, all certificates would otherwise have to be re-issued, which can hit the rate limit.</li>
<li><strong>Lifetimes are shrinking industry-wide:</strong> the CA/Browser bodies have decided to reduce the maximum certificate lifetime step by step – from 200 days (2026) via 100 days (2027) down to 47 days from 2029. Let&rsquo;s Encrypt&rsquo;s 90 days aren&rsquo;t stingy, they&rsquo;re the future for everyone. If you automate today, you won&rsquo;t notice any of it.</li>
</ul>
]]></content:encoded></item></channel></rss>