<?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>Safari on Kabelbruch</title><link>https://kabelbruch.de/tags/safari/</link><description>Recent content in Safari on Kabelbruch</description><image><title>Kabelbruch</title><url>https://kabelbruch.de/og-image.png</url><link>https://kabelbruch.de/og-image.png</link></image><generator>Hugo</generator><language>de-DE</language><lastBuildDate>Wed, 04 Nov 2026 21:15:00 +0100</lastBuildDate><atom:link href="https://kabelbruch.de/tags/safari/index.xml" rel="self" type="application/rss+xml"/><item><title>Mein eigener Artikel stand auf der Sperrliste</title><link>https://kabelbruch.de/posts/werbeblocker-sperrt-eigenen-artikel/</link><pubDate>Wed, 04 Nov 2026 21:15:00 +0100</pubDate><guid>https://kabelbruch.de/posts/werbeblocker-sperrt-eigenen-artikel/</guid><description>Ein Werbeblocker sperrte genau einen Artikel auf Kabelbruch. Schuld war kein Tracker, sondern ein Wort im Slug. Wie ich die Regel gefunden habe.</description><content:encoded><![CDATA[<p>Ein Leser hat mir auf Mastodon geschrieben. Er fand den Blog interessant, aber ein Artikel ließ sich bei ihm nicht öffnen. Alle anderen schon. Safari meldete nur, die Seite werde „von einer Inhaltssperre blockiert“. Er schrieb selbst, das sei wohl eher ein Fun-Fact. Etwas später kam der Nachtrag, dass er unter macOS mit Safari und dem Werbeblocker <strong>wBlock</strong> unterwegs ist.</p>
<p>Der betroffene Artikel war ausgerechnet der über <a href="/posts/besucherstatistik-matomo-log-import/">Besucherstatistik ohne Cookie-Banner</a>. Also genau der Artikel, in dem ich erkläre, warum hier kein Tracking-Skript läuft.</p>
<p>Mein erster Gedanke: Das darf doch nicht wahr sein. Ausgerechnet ein Artikel über die Erfassung von Besucherdaten, bei dem ich extrem darauf achte, datensparsam zu sein, wird geblockt.</p>
<h2 id="mein-erster-verdacht">Mein erster Verdacht</h2>
<p>Der Artikel war der einzige mit „matomo“ in der Adresse. Viele Blocklisten haben Regeln gegen Matomo, weil Matomo meistens als Tracking-Skript im Browser läuft. Bei mir läuft es zwar nur auf dem Server und wertet Logdateien aus, aber das weiß eine Filterliste nicht. Das klang plausibel, und so habe ich es auch geantwortet.</p>
<p>Es war falsch. Aber dazu brauchte ich erst die Liste der aktiven Filter. Der Leser hatte diese sieben aktiv:</p>
<ul>
<li>AdGuard Base Filter</li>
<li>AdGuard Tracking Protection Filter</li>
<li>AdGuard URL Tracking Protection Filter</li>
<li>Actually Legitimate URL Shortener Tool</li>
<li>Peter Lowe&rsquo;s Blocklist</li>
<li>AdGuard Cookie Notices</li>
<li>AdGuard German Filter</li>
</ul>
<h2 id="suchen-in-300000-zeilen">Suchen in 300.000 Zeilen</h2>
<p>Die AdGuard-Listen liegen offen auf GitHub im Repo <a href="https://github.com/AdguardTeam/FiltersRegistry" target="_blank" rel="noopener noreferrer">AdguardTeam/FiltersRegistry</a>, auch in der Fassung für Safari. Zusammen sind das gut 300.000 Zeilen. Ich habe sie heruntergeladen und nach den Wörtern aus der Adresse durchsucht: <code>matomo</code>, <code>log-import</code>, <code>besucherstatistik</code>, <code>cookie-banner</code>.</p>
<p>Bei <code>matomo</code> kam nichts, was eine Seite sperrt. Die Treffer waren nur Regeln, die auf bestimmten fremden Seiten Cookie-Dialoge wegklicken. Bei <code>cookie-banner</code> dagegen stand in der Liste <strong>AdGuard Cookie Notices</strong> diese Zeile:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-cookie-banner-$domain=~killthecookiebanner.eu
</span></span></code></pre></div><p>Kurz übersetzt:</p>
<ul>
<li><code>-cookie-banner-</code> ist das Muster. Es hat keinen Anker am Anfang oder Ende, passt also auf <strong>jede</strong> Adresse, in der diese Zeichenfolge irgendwo vorkommt.</li>
<li><code>$domain=~killthecookiebanner.eu</code> heißt: überall, außer auf dieser einen Domain. Die Tilde ist ein „nicht“.</li>
</ul>
<p>Gedacht ist die Regel für Dateien wie <code>/js/site-cookie-banner-v2.js</code>. Mein Slug, also der Teil der Adresse, der den Artikel benennt, sah aber so aus:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">/posts/besucherstatistik-ohne-cookie-banner-matomo-log-import/
</span></span></code></pre></div><p>Da steht es: <code>-ohne-cookie-banner-matomo</code>. Bindestrich, cookie, Bindestrich, banner, Bindestrich. Die Regel hat keine Einschränkung auf Skripte oder Bilder, und wBlock hat damit in Safari gleich die ganze Seite gesperrt. Kein anderer Artikel hat diese Zeichenfolge in der Adresse, deshalb ging alles andere.</p>
<p>In Safari nachgestellt habe ich das nicht. Ob andere Werbeblocker mit derselben Liste auch das ganze Dokument sperren oder nur Dateien, habe ich ebenfalls nicht geprüft.</p>
<p>Die Ironie gefällt mir trotzdem: Ein Filter gegen Cookie-Banner sperrt einen Artikel darüber, wie man ohne Cookie-Banner auskommt.</p>
<h2 id="die-lösung-neue-adresse">Die Lösung: neue Adresse</h2>
<p>Der Inhalt war nie das Problem, nur die Adresse. Filterlisten wie diese sehen den Text eines Artikels gar nicht, sie prüfen URLs. Also bekam der Artikel einen neuen Slug ohne die verdächtige Zeichenfolge. Im Front Matter sieht das so aus:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">slug</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;besucherstatistik-matomo-log-import&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">aliases</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;/posts/besucherstatistik-ohne-cookie-banner-matomo-log-import/&#34;</span><span class="p">]</span><span class="w">
</span></span></span></code></pre></div><p>Mit <code>aliases</code> legt Hugo unter der alten Adresse eine kleine Seite an, die per Meta-Refresh auf die neue weiterleitet. Die Seite enthält kein JavaScript, an der Content-Security-Policy ändert sich also nichts. Links, die schon im Umlauf sind, funktionieren damit weiter.</p>
<p>Mit einer Einschränkung: Wer die Filterliste aktiv hat, bleibt auf der alten Adresse hängen. Die Weiterleitungsseite liegt ja genau dort, wo die Regel greift. Diesen Lesern hilft nur der neue Link.</p>
<p>Drei andere Artikel haben auf den Matomo-Artikel verlinkt. Die zeigen jetzt direkt auf die neue Adresse statt über die Weiterleitung.</p>
<p>Eine Sorge hatte ich noch: Meine Pipeline <a href="/posts/blog-tootet-selbst-mastodon-hugo/">kündigt neue Artikel selbst auf Mastodon an</a>, und sie merkt sich das über die Adresse. Für sie war der Matomo-Artikel unter der neuen Adresse also ein „neuer“ Artikel. Getootet hat sie trotzdem nicht, weil sie nur Artikel ankündigt, die höchstens drei Tage alt sind. Da hat mich eine Schutzregel vor einem doppelten Toot bewahrt, an die ich bei diesem Problem gar nicht gedacht hatte.</p>
<p>Kurz nach der Umstellung kam die Rückmeldung vom Leser: Die Seite lässt sich jetzt „ohne Murren“ öffnen.</p>
<h2 id="ein-grober-check-für-neue-slugs">Ein grober Check für neue Slugs</h2>
<p>Damit mir das nicht noch einmal passiert, habe ich ein kleines Skript, das eine Adresse gegen die Listen prüft. Es ist kein Nachbau eines Werbeblockers. Es kennt nur einfache URL-Regeln, ignoriert Ausnahmen und die meisten Optionen. Für die Frage „Steht in meinem Slug etwas, worauf eine Liste anspringt?“ reicht es.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env python3</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;Grober Check: Welche einfache Filterregel passt auf eine URL?
</span></span></span><span class="line"><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="cl"><span class="s2">Kein vollständiger Adblocker-Nachbau. Er kennt nur einfache URL-Regeln,
</span></span></span><span class="line"><span class="cl"><span class="s2">ignoriert Ausnahmen (@@) und die meisten Optionen.
</span></span></span><span class="line"><span class="cl"><span class="s2">&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">re</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">sys</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">urllib.request</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">BASE</span> <span class="o">=</span> <span class="s2">&#34;https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/platforms/extension/safari/filters/&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">LISTS</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;AdGuard Base&#34;</span><span class="p">:</span> <span class="s2">&#34;2.txt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;AdGuard Tracking Protection&#34;</span><span class="p">:</span> <span class="s2">&#34;3.txt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;AdGuard Cookie Notices&#34;</span><span class="p">:</span> <span class="s2">&#34;18.txt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;AdGuard German&#34;</span><span class="p">:</span> <span class="s2">&#34;6.txt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Peter Lowe&#39;s Blocklist&#34;</span><span class="p">:</span> <span class="s2">&#34;204.txt&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">SKIP_OPTIONS</span> <span class="o">=</span> <span class="p">(</span><span class="s2">&#34;third-party&#34;</span><span class="p">,</span> <span class="s2">&#34;script&#34;</span><span class="p">,</span> <span class="s2">&#34;image&#34;</span><span class="p">,</span> <span class="s2">&#34;stylesheet&#34;</span><span class="p">,</span> <span class="s2">&#34;xmlhttprequest&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;font&#34;</span><span class="p">,</span> <span class="s2">&#34;media&#34;</span><span class="p">,</span> <span class="s2">&#34;subdocument&#34;</span><span class="p">,</span> <span class="s2">&#34;permissions&#34;</span><span class="p">,</span> <span class="s2">&#34;removeparam&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">to_regex</span><span class="p">(</span><span class="n">pattern</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">pattern</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;||&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">rx</span><span class="p">,</span> <span class="n">pattern</span> <span class="o">=</span> <span class="sa">r</span><span class="s2">&#34;^[a-z]+://([^/]*\.)?&#34;</span><span class="p">,</span> <span class="n">pattern</span><span class="p">[</span><span class="mi">2</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">pattern</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&#34;|&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">rx</span><span class="p">,</span> <span class="n">pattern</span> <span class="o">=</span> <span class="s2">&#34;^&#34;</span><span class="p">,</span> <span class="n">pattern</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">rx</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">pattern</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">c</span> <span class="o">==</span> <span class="s2">&#34;*&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">rx</span> <span class="o">+=</span> <span class="s2">&#34;.*&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">elif</span> <span class="n">c</span> <span class="o">==</span> <span class="s2">&#34;^&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">rx</span> <span class="o">+=</span> <span class="sa">r</span><span class="s2">&#34;([^\w.%-]|$)&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">rx</span> <span class="o">+=</span> <span class="n">re</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">rx</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">text</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">text</span><span class="o">.</span><span class="n">splitlines</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Kommentare, Ausnahmen, Regex-Regeln und kosmetische Regeln überspringen</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">line</span> <span class="ow">or</span> <span class="n">line</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="ow">in</span> <span class="s2">&#34;![@/&#34;</span> <span class="ow">or</span> <span class="s2">&#34;#&#34;</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="n">pattern</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">partition</span><span class="p">(</span><span class="s2">&#34;$&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">&#34;~&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">SKIP_OPTIONS</span><span class="p">)</span> <span class="k">for</span> <span class="n">o</span> <span class="ow">in</span> <span class="n">options</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;,&#34;</span><span class="p">)</span> <span class="k">if</span> <span class="n">o</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s2">&#34;domain=&#34;</span> <span class="ow">in</span> <span class="n">options</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">&#34;domain=~&#34;</span><span class="p">,</span> <span class="n">options</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="k">continue</span>  <span class="c1"># Regel gilt nur für bestimmte fremde Domains</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">4</span> <span class="ow">and</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="n">to_regex</span><span class="p">(</span><span class="n">pattern</span><span class="p">),</span> <span class="n">url</span><span class="p">,</span> <span class="n">re</span><span class="o">.</span><span class="n">I</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="n">line</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">url</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:]:</span>
</span></span><span class="line"><span class="cl">    <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;== </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">LISTS</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="k">with</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">BASE</span> <span class="o">+</span> <span class="n">file</span><span class="p">)</span> <span class="k">as</span> <span class="n">r</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">check</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">r</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s2">&#34;utf-8&#34;</span><span class="p">,</span> <span class="s2">&#34;ignore&#34;</span><span class="p">))</span>
</span></span></code></pre></div><p>Aufgerufen mit der alten und der neuen Adresse:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-console" data-lang="console"><span class="line"><span class="cl"><span class="gp">$</span> python3 slug-check.py <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="go">    https://example.com/posts/besucherstatistik-ohne-cookie-banner-matomo-log-import/ \
</span></span></span><span class="line"><span class="cl"><span class="go">    https://example.com/posts/besucherstatistik-matomo-log-import/
</span></span></span><span class="line"><span class="cl"><span class="go">== https://example.com/posts/besucherstatistik-ohne-cookie-banner-matomo-log-import/
</span></span></span><span class="line"><span class="cl"><span class="go">AdGuard Cookie Notices: -cookie-banner-$domain=~killthecookiebanner.eu
</span></span></span><span class="line"><span class="cl"><span class="go">== https://example.com/posts/besucherstatistik-matomo-log-import/
</span></span></span></code></pre></div><p>Die alte Adresse trifft genau eine Regel, die neue keine. Die Listen ändern sich ständig, das Ergebnis gilt also nur für den Tag, an dem man prüft. Bei mir war das der 4. November 2026.</p>
<h2 id="was-ich-daraus-mitnehme">Was ich daraus mitnehme</h2>
<p><strong>Der erste Verdacht war plausibel und trotzdem falsch.</strong> „matomo in der Adresse“ klang so logisch, dass ich es fast nicht geprüft hätte. Erst die Liste der aktiven Filter und ein stumpfes Durchsuchen haben die echte Ursache gezeigt.</p>
<p><strong>Auch Adressen müssen durch Filter.</strong> Ich achte bei Adressen auf Lesbarkeit und Suchbegriffe. Dass sie auch durch Filterlisten müssen, die nur Zeichenketten vergleichen, hatte ich nicht auf dem Schirm. Wörter wie <code>cookie-banner</code>, <code>tracking</code> oder <code>ads</code> mit Bindestrichen drumherum sind in einer Adresse riskant.</p>
<p><strong>Meldungen von Lesern sind Gold wert.</strong> Ohne die Nachricht hätte ich das nie bemerkt. In meinen Logs taucht ein gesperrter Abruf gar nicht auf, weil der Browser die Anfrage nie abschickt. Danke an den Leser, der sich die Mühe gemacht hat.</p>
<h2 id="wie-es-weitergeht">Wie es weitergeht</h2>
<p>Bei AdGuard melde ich das nicht. Die Regel ist breit, aber ich habe meine Seite umgebaut und achte in Zukunft selbst darauf, was in meinen Adressen steht.</p>
<p>Dafür überlege ich, den Slug-Check als Schritt in die Pipeline zu hängen, so wie die Prüfung der CSP-Hashes. Dann würde mir ein neuer Artikel mit verdächtiger Adresse schon vor der Veröffentlichung auffallen und nicht erst, wenn jemand auf Mastodon nachfragt.</p>
]]></content:encoded></item></channel></rss>