<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harish Annam</title>
    <description>The latest articles on DEV Community by Harish Annam (@harish_annam).</description>
    <link>https://dev.to/harish_annam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F144067%2F7604f7b5-3ffa-411c-8d07-6068e5f0ea4f.png</url>
      <title>DEV Community: Harish Annam</title>
      <link>https://dev.to/harish_annam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harish_annam"/>
    <language>en</language>
    <item>
      <title>Self-hosted Umami still gets blocked by adblockers if your subdomain is named umami</title>
      <dc:creator>Harish Annam</dc:creator>
      <pubDate>Tue, 21 Jul 2026 06:15:02 +0000</pubDate>
      <link>https://dev.to/harish_annam/self-hosted-umami-still-gets-blocked-by-adblockers-if-your-subdomain-is-named-umami-3ni5</link>
      <guid>https://dev.to/harish_annam/self-hosted-umami-still-gets-blocked-by-adblockers-if-your-subdomain-is-named-umami-3ni5</guid>
      <description>&lt;p&gt;I self-host &lt;a href="https://umami.is" rel="noopener noreferrer"&gt;Umami&lt;/a&gt; for my SaaS, &lt;a href="https://parserbee.com" rel="noopener noreferrer"&gt;ParserBee&lt;/a&gt;. The main reason I picked it: privacy-friendly, cookie-less, first-party analytics that adblockers supposedly leave alone because the script comes from your own domain.&lt;/p&gt;

&lt;p&gt;That last assumption turned out to be wrong, and the reason is the subdomain name itself. Writing it up because the fix is small and the failure mode is silent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;My Umami instance ran at &lt;code&gt;umami.parserbee.com&lt;/code&gt;, so the tracker was loaded like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://umami.parserbee.com/script.js"&lt;/span&gt; &lt;span class="na"&gt;data-website-id=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The EasyPrivacy filter list (used by uBlock Origin, Brave Shields, AdGuard, and most other blockers) contains a rule that matches the Umami tracker by hostname pattern, along the lines of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;||umami.*/script.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It doesn't target a specific company's server. It targets any host whose subdomain is literally named &lt;code&gt;umami&lt;/code&gt; serving a file called &lt;code&gt;script.js&lt;/code&gt;. Which is exactly how most of us name things when self-hosting: &lt;code&gt;umami.mydomain.com&lt;/code&gt;, &lt;code&gt;plausible.mydomain.com&lt;/code&gt;, &lt;code&gt;matomo.mydomain.com&lt;/code&gt;. The filter lists know this convention, and they have rules for it.&lt;/p&gt;

&lt;p&gt;The result: every visitor with an adblocker never loads the script. No errors on your side, no console noise, nothing in the Umami logs. The traffic just quietly never shows up. I only caught it because signups were arriving from campaigns that Umami claimed nobody clicked; the server logs and Stripe disagreed with the analytics, and the server logs were right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Serve the same Umami instance from a second hostname that no filter list matches. No proxying, no renaming files, no changes to the Umami install itself.&lt;/p&gt;

&lt;p&gt;I added &lt;code&gt;u.parserbee.com&lt;/code&gt; as an alias for the same service:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. DNS record.&lt;/strong&gt; A CNAME (or A record) for &lt;code&gt;u.parserbee.com&lt;/code&gt; pointing at the same server as the existing &lt;code&gt;umami.parserbee.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Reverse proxy.&lt;/strong&gt; Add the new hostname to the existing Umami site config so both route to the same instance. I run Umami in Docker behind Coolify, where this is just adding a second domain to the service; on Nginx, Caddy or Traefik it's one extra hostname in the site block. TLS certs get issued automatically in all of these.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Script tag.&lt;/strong&gt; Change only the &lt;code&gt;src&lt;/code&gt;. The website id stays the same, so historical data and new data land in the same place with no migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script
  &lt;/span&gt;&lt;span class="na"&gt;defer&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://u.parserbee.com/script.js"&lt;/span&gt;
  &lt;span class="na"&gt;data-website-id=&lt;/span&gt;&lt;span class="s"&gt;"YOUR-WEBSITE-ID"&lt;/span&gt;
  &lt;span class="na"&gt;data-domains=&lt;/span&gt;&lt;span class="s"&gt;"app.parserbee.com"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard can keep living at &lt;code&gt;umami.parserbee.com&lt;/code&gt;. Blocklists only affect the tracker request in visitors' browsers; visiting your own dashboard is unaffected.&lt;/p&gt;

&lt;p&gt;(&lt;code&gt;data-domains&lt;/code&gt; is unrelated to the blocking issue, but set it anyway: it stops localhost and preview deployments from writing into production stats.)&lt;/p&gt;

&lt;p&gt;After deploying, tracked page views immediately lined up with what the server logs had been showing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is working around a filter list okay?
&lt;/h2&gt;

&lt;p&gt;Worth addressing directly. EasyPrivacy exists to block cross-site tracking, fingerprinting, and ad networks. Self-hosted Umami is none of those: first-party, no cookies, no cross-site anything, data stays on my own server. The rule catches it because of a naming convention, not because of what the software does. I see renaming a subdomain for first-party privacy-respecting analytics as fixing a false positive. If you're proxying an actual ad-tech tracker through your domain to defeat blockers, that's a different thing, and the lists tend to catch popular patterns for that eventually anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Self-hosting alone no longer hides analytics from filter lists. EasyPrivacy has hostname-pattern rules for Umami, Plausible, Matomo, and others.&lt;/li&gt;
&lt;li&gt;Don't name the tracking subdomain after the tool. &lt;code&gt;u.&lt;/code&gt;, &lt;code&gt;stats.&lt;/code&gt;, &lt;code&gt;data.&lt;/code&gt;, anything generic is fine; &lt;code&gt;umami.&lt;/code&gt;, &lt;code&gt;plausible.&lt;/code&gt;, and &lt;code&gt;analytics.&lt;/code&gt; are all matched or will be.&lt;/li&gt;
&lt;li&gt;The fix is an alias hostname on the same service plus a one-line script change. No data migration, since the website id never changes.&lt;/li&gt;
&lt;li&gt;If your analytics look low, check with uBlock Origin enabled before doubting your marketing. I doubted my campaigns for longer than the fix took.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I hit this while building &lt;a href="https://parserbee.com" rel="noopener noreferrer"&gt;ParserBee&lt;/a&gt;, an AI document parser that turns PDFs, images, and Office documents into structured JSON. If you ever need to pull clean data out of invoices or resumes without writing parsing code, take a look.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>webdev</category>
      <category>selfhosted</category>
      <category>umami</category>
    </item>
  </channel>
</rss>
