<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Blog — George Mishurovsky: posts tagged ubuntu</title>
<link>https://mishurovsky.com/blog/?go=tags/ubuntu/</link>
<description>A blog by George Mishurovsky — a senior software engineer with a medical degree. Drawing from both engineering and scientific thinking, he explores software, architecture, design, psychology, and product thinking.</description>
<author></author>
<language>en</language>
<generator>Aegea 11.3 (v4134e)</generator>

<itunes:owner>
<itunes:name></itunes:name>
<itunes:email>george@mishurovsky.com</itunes:email>
</itunes:owner>
<itunes:subtitle>A blog by George Mishurovsky — a senior software engineer with a medical degree. Drawing from both engineering and scientific thinking, he explores software, architecture, design, psychology, and product thinking.</itunes:subtitle>
<itunes:image href="https://mishurovsky.com/blog/pictures/userpic/userpic-square@2x.jpg?1753619610" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>Useful Commands to Debug DNS Issues and Redirection Loops (Ubuntu, Apache, Letsencrypt)</title>
<guid isPermaLink="false">1</guid>
<link>https://mishurovsky.com/blog/?go=all/diagnosing-dns-and-redirection-issues-ubuntu-apache-letsencrypt/</link>
<pubDate>Sun, 27 Jul 2025 20:32:29 +0200</pubDate>
<author></author>
<comments>https://mishurovsky.com/blog/?go=all/diagnosing-dns-and-redirection-issues-ubuntu-apache-letsencrypt/</comments>
<description>
&lt;p&gt;Recently I decided to add a blog to my personal website. Now I can regularly share thoughts with everyone! The blog resides at &lt;a href="https://mishurovsky.com/blog/,"&gt;https://mishurovsky.com/blog/,&lt;/a&gt; while the main page remains at &lt;a href="https://mishurovsky.com."&gt;https://mishurovsky.com.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The path to a working solution, however, was not easy. My website is a Next.js application hosted on Vercel, while the blog engine I chose is a PHP application requiring a separate Linux server with Apache and MariaDB. My initial intent was to lead all traffic through a self-hosted reverse proxy, which would direct /blog requests to the blog engine, and all other requests to the Vercel page. I did not manage to make it work, so I ended up hosting both PHP and Next.js apps from my own server.&lt;/p&gt;
&lt;p&gt;But that wasn’t the end of the story. After migration, HTTP connection was OK, but whenever I tried to switch to HTTPS, the response was still served by Vercel, even though I deleted both my domain and project from there! It was not just a failing request, but a redirection loop, 308 to mishurovsky.com again and again and again. I spent two days debugging configs and waiting for DNS caches to clear.&lt;/p&gt;
&lt;p&gt;Long story short, the problem was in my use of Letsencrypt: I launched it as&lt;/p&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;certbot --apache ...# instead of certbot certonly --apache ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;, and Certbot created a separate HTTPS virtual host that reused an old configuration pointing to Vercel’s upstream. Apache prioritized this config over mine, causing a persistent redirect loop.&lt;/p&gt;
&lt;h2&gt;Helpful Commands to Debug DNS Problems and Redirection Loops&lt;/h2&gt;
&lt;p&gt;During this weekend journey, I discovered a lot of valuable commands to debug connectivity problems, which I want to share. Hopefully, these will help you, my reader, or me myself in some future.&lt;/p&gt;
&lt;h3&gt;1. Verbose cURL&lt;/h3&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;curl -v https://example.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Connect to a website and get a &lt;b&gt;verbose&lt;/b&gt; response. Helps to see HTTP response codes and SSL connectivity status.&lt;/p&gt;
&lt;h3&gt;2. cURL with redirects&lt;/h3&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;curl -I -L https://example.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Connect to a website and get only &lt;b&gt;headers&lt;/b&gt; in response + &lt;b&gt;follow redirects&lt;/b&gt;. I used this countless times to examine if redirect loops persist.&lt;/p&gt;
&lt;h3&gt;3. Get site IP&lt;/h3&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;dig +short example.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Get an ip for a website. Useful when checking if a website is indeed hosted from a rented server.&lt;/p&gt;
&lt;h3&gt;4. Show all Letsencrypt certificates&lt;/h3&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;certbot certificates&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;List all Letsencrypt certificates on a server with their domains, expiration dates, and paths to private and public keys.&lt;/p&gt;
&lt;h3&gt;5. Review Apache config&lt;/h3&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;apache2ctl -S&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All the main Apache config details: virtual hosts and structure. The most interesting part is error section in the beginning of the output — this is where I found a reference to an additional config from Letsencrypt.&lt;/p&gt;
&lt;h3&gt;6. Examine Port Usage&lt;/h3&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;ss -tuln | grep &amp;#039;:443&amp;#039;
lsof -i :443&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;List all listening sockets (TCP/UDP), showing if port 443 is open. Then get a list of all process on port 443 (HTTPS).&lt;/p&gt;
&lt;h3&gt;7. Flush DNS Cache&lt;/h3&gt;
&lt;p&gt;Finally, Mac OS specific:&lt;/p&gt;
&lt;div class="e2-code-block" data-language="shell"&gt;&lt;div class="e2-code-header"&gt;&lt;span class="e2-code-language"&gt;Shell&lt;/span&gt;&lt;button class="e2-code-copy" type="button" aria-label="Copy code to clipboard" data-copy-text="Copy" data-copied-text="Copied!" data-failed-text="Failed"&gt;&lt;span class="e2-svgi"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"&gt;&lt;mask id="cutout"&gt;&lt;rect width="100%" height="100%" fill="white"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" stroke-width="1.33" rx="1" fill="black" stroke="black"/&gt;&lt;/mask&gt;&lt;rect x="5.25" y="1.75" width="9" height="9" rx="1" stroke-width="1.33" fill="none" mask="url(#cutout)"/&gt;&lt;rect x="1.75" y="5.25" width="9" height="9" rx="1" stroke-width="1.33" fill="none"/&gt;&lt;/svg&gt;
&lt;/span&gt;Copy&lt;/button&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class="hljs language-shell"&gt;sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These two commands flush DNS cache on modern Macs, so DNS could be tested after updates. Requires to quit and re-start a browser after execution.&lt;/p&gt;
</description>
</item>


</channel>
</rss>