<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://brokenco.de//feed/by_tag/nginx.xml" rel="self" type="application/atom+xml" /><link href="https://brokenco.de//" rel="alternate" type="text/html" /><updated>2026-05-03T00:12:50+00:00</updated><id>https://brokenco.de//feed/by_tag/nginx.xml</id><title type="html">rtyler</title><subtitle>a moderately technical blog</subtitle><author><name>R. Tyler Croy</name></author><entry><title type="html">Reverse proxying a Tide application with Nginx</title><link href="https://brokenco.de//2021/02/16/async-h1-error-tide.html" rel="alternate" type="text/html" title="Reverse proxying a Tide application with Nginx" /><published>2021-02-16T00:00:00+00:00</published><updated>2021-02-16T00:00:00+00:00</updated><id>https://brokenco.de//2021/02/16/async-h1-error-tide</id><content type="html" xml:base="https://brokenco.de//2021/02/16/async-h1-error-tide.html"><![CDATA[<p>Every now and again I’ll encounter a silly problem, fix it, forget about it,
and then later run into the <em>exact</em> same problem again. Today’s example is a
confusing error I encountered when reverse-proxying a
<a href="https://github.com/http-rs/tide">Tide</a> application with
<a href="https://nginx.org">Nginx</a>. In the Tide application, I was greeted with an ever-so-descriptive error:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR tide::listener::tcp_listener &gt; async-h1 error
</code></pre></div></div>

<p>I <em>knew</em> that I had hit this before, because I
remember discussing with <a href="https://github.com/jbr">jbr</a> the need for
<a href="https://github.com/http-rs/async-h1">async-h1</a> to be a bit more verbose in its
error messages. Try as I might however, I was not able to remember how to solve
the problem  Looking at the Nginx error logs, which weren’t that much more helpful:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2021/02/14 17:41:26 [error] 45774#101333: *53 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.1, server: dotdotvote.com, request: "GET / HTTP/1.1", upstream: "http://10.0.1.6:8000/", host: "dotdotvote.com"
2021/02/14 17:41:26 [error] 45774#101333: *53 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.1, server: dotdotvote.com, request: "GET /favicon.ico HTTP/1.1", upstream: "http://10.0.1.6:8000/favicon.ico", host: "dotdotvote.com", referrer: "https://dotdotvote.com/"
</code></pre></div></div>

<p>Eventually I stumbled into <a href="https://github.com/http-rs/tide/issues/458">this closed GitHub
issue</a> which had the critical
missing configuration snippet I was missing: <code class="language-plaintext highlighter-rouge">proxy_http_version 1.1;</code> It seems
that the <code class="language-plaintext highlighter-rouge">async-h1</code> crate cannot parse HTTP 1.0, or 2.0, or some version of
HTTP other than 1.1. I’m honestly not really sure what format was being sent
along to the Tide application.</p>

<p>My full <code class="language-plaintext highlighter-rouge">location</code> block below for reference:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host            $host;
    proxy_set_header X-Real-IP       $remote_addr;
    # https://github.com/http-rs/tide/issues/458#issuecomment-619243201
    proxy_http_version 1.1;

    proxy_pass http://dotdotvote;
  }
</code></pre></div></div>

<p>With that one weird trick, Nginx is happily reverse-proxying my little Tide
application!</p>]]></content><author><name>R. Tyler Croy</name></author><category term="nginx" /><category term="rust" /><summary type="html"><![CDATA[Every now and again I’ll encounter a silly problem, fix it, forget about it, and then later run into the exact same problem again. Today’s example is a confusing error I encountered when reverse-proxying a Tide application with Nginx. In the Tide application, I was greeted with an ever-so-descriptive error:]]></summary></entry><entry><title type="html">Multiple Let’s Encrypt domains in a single Nginx server block</title><link href="https://brokenco.de//2021/02/15/dynamic-certificates-nginx.html" rel="alternate" type="text/html" title="Multiple Let’s Encrypt domains in a single Nginx server block" /><published>2021-02-15T00:00:00+00:00</published><updated>2021-02-15T00:00:00+00:00</updated><id>https://brokenco.de//2021/02/15/dynamic-certificates-nginx</id><content type="html" xml:base="https://brokenco.de//2021/02/15/dynamic-certificates-nginx.html"><![CDATA[<p><a href="https://nginx.org">Nginx</a> is a fantastic web server and reverse proxy to use
with <a href="https://letsencrypt.org/">Let’s Encrypt</a>, but when dealing with multiple
domains it can be a bit tedious to configure.  I have been moving services into
more <a href="https://freebsd.org">FreeBSD</a> jails as I alluded to <a href="/2021/02/02/freebsd-pkg-with-an-offline-jail.html">in my previous
post</a>, among them the
general Nginx proxy jail which I have serving my HTTP-based services. Using
Let’s Encrypt for TLS, I found myself declaring multiple <code class="language-plaintext highlighter-rouge">server</code> blocks inside
my virtual host configurations to handle the apex domain (e.g.
<code class="language-plaintext highlighter-rouge">dotdotvote.com</code>), the <code class="language-plaintext highlighter-rouge">www</code> subdomain, and vanity domains (e.g.
<code class="language-plaintext highlighter-rouge">dotdot.vote</code>). With the help <code class="language-plaintext highlighter-rouge">Membear</code> and <code class="language-plaintext highlighter-rouge">MTecknology</code> in the <code class="language-plaintext highlighter-rouge">#nginx</code>
channel on <a href="https://freenode.net">Freenode</a>, I was able to refactor multiple
largely redundant <code class="language-plaintext highlighter-rouge">server</code> blocks into one.</p>

<p>With Nginx 1.15.9 a feature I have seen referred to as “dynamic certificates”
was released. Originally the <code class="language-plaintext highlighter-rouge">ssl_certificate</code> and <code class="language-plaintext highlighter-rouge">ssl_certificate_key</code> were
loaded when Nginx <em>started</em>. This meant that you could not refer to any of the
<a href="http://nginx.org/en/docs/varindex.html">Nginx variables</a> when creating the
setting. With dynamic certificates, the resolution of the <code class="language-plaintext highlighter-rouge">ssl_certificate</code>
directive is done <em>later</em> by the worker(s) process(es). It’s a <em>very</em> handy
feature!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name dotdotvote.com www.dotdotvote.com dotdot.vote;
  ##
  # Certificates
  ssl_certificate     /usr/local/etc/letsencrypt/live/$ssl_server_name/fullchain.pem;
  ssl_certificate_key /usr/local/etc/letsencrypt/live/$ssl_server_name/privkey.pem;

  # ... snip ...
  location / {
    # ... snip ...
    proxy_pass http://dotdotvote;
  }
}
</code></pre></div></div>

<p>In the example above, I’m using the <code class="language-plaintext highlighter-rouge">$ssl_server_name</code> variable which will
correspond to the server name requested in the SNI part of the TLS payload.
This ensures that the right hostname’s certificate is utilized.</p>

<p>My first attempt was with <code class="language-plaintext highlighter-rouge">$server_name</code>, which I recommend you avoid using
<code class="language-plaintext highlighter-rouge">$server_name</code> since that will not be computed per request. For example, in the
block below the <code class="language-plaintext highlighter-rouge">$server_name</code> variable will <em>always</em> be <code class="language-plaintext highlighter-rouge">dotdotvote.com</code> and
requests served on <code class="language-plaintext highlighter-rouge">www.dotdotvote.com</code> will use the incorrect certificate.:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
    server_name dotdotvote.com www.dotdotvote.com;

    ssl_certificate '/some/path/$server_name/fullchain.pem';
}
</code></pre></div></div>

<p>When I was originally setting this up, I also stumbled into some “Permission
denied” errors from Nginx. With the static certificate declaration, the <em>main</em>
Nginx process would load the file. That process would run as root before
dropping privileges to the <code class="language-plaintext highlighter-rouge">www</code> user for the Nginx worker(s). To address this
I needed to go change filesystem ownership in order for the <code class="language-plaintext highlighter-rouge">www</code> user to
properly read the certificate files.</p>

<hr />

<p>In retrospect, this feature seems relatively simple to use if you have a good
understanding of the Nginx process and permissions model. Suffice it to say, I
wouldn’t have figured this out without a bit of help from the folks in
<code class="language-plaintext highlighter-rouge">#nginx</code>. With the change in place my Nginx configurations are now much more
succinct and readable!</p>]]></content><author><name>R. Tyler Croy</name></author><category term="nginx" /><category term="freebsd" /><category term="security" /><summary type="html"><![CDATA[Nginx is a fantastic web server and reverse proxy to use with Let’s Encrypt, but when dealing with multiple domains it can be a bit tedious to configure. I have been moving services into more FreeBSD jails as I alluded to in my previous post, among them the general Nginx proxy jail which I have serving my HTTP-based services. Using Let’s Encrypt for TLS, I found myself declaring multiple server blocks inside my virtual host configurations to handle the apex domain (e.g. dotdotvote.com), the www subdomain, and vanity domains (e.g. dotdot.vote). With the help Membear and MTecknology in the #nginx channel on Freenode, I was able to refactor multiple largely redundant server blocks into one.]]></summary></entry></feed>