Files
2026-03-29 17:20:43 +02:00

620 lines
24 KiB
HTML
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html
lang="de-DE"
dir="ltr"
class="h-full">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="language" content="de-DE">
<script>
(function () {
const storedTheme = localStorage.getItem("theme");
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
document.documentElement.setAttribute("data-theme", theme);
})();
</script>
<title>Blocking Invalid Recipients Before They Reach Your Exchange Server | Demians Blog</title>
<meta
name="description"
content="
Recently, I had to deal with a serious problem: backscatter.
One of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …
">
<link rel="canonical" href="https://pyte.dev/posts/blocking-invalid-rcpt-postfix/">
<meta name="robots" content="index, follow">
<meta property="og:type" content="article">
<meta property="og:title" content="Blocking Invalid Recipients Before They Reach Your Exchange Server | Demians Blog">
<meta property="og:description" content="Recently, I had to deal with a serious problem: backscatter.
One of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …">
<meta property="og:url" content="https://pyte.dev/posts/blocking-invalid-rcpt-postfix/">
<meta property="og:site_name" content="Demians Blog"><meta property="og:image" content="https://pyte.dev/assets/patrick.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630"><meta property="article:published_time" content="2025-08-01T10:03:15&#43;02:00">
<meta
property="article:modified_time"
content="2025-08-01T10:03:15&#43;02:00">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Blocking Invalid Recipients Before They Reach Your Exchange Server | Demians Blog">
<meta name="twitter:description" content="Recently, I had to deal with a serious problem: backscatter.
One of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …"><meta name="twitter:image" content="https://pyte.dev/assets/patrick.png">
<script type="application/ld+json">
"{\"@context\":\"https://schema.org\",\"@type\":\"BlogPosting\",\"author\":{\"@type\":\"Person\",\"email\":\"demian (at) pyte (dot) dev\",\"name\":\"Demians Blog\"},\"dateModified\":\"2025-08-01T10:03:15+02:00\",\"datePublished\":\"2025-08-01T10:03:15+02:00\",\"description\":\"Recently, I had to deal with a serious problem: backscatter.\\nOne of our mail gateways ended up listed on the backscatter.org blacklist for sending bounce …\",\"headline\":\"Blocking Invalid Recipients Before They Reach Your Exchange Server\",\"image\":\"https://pyte.dev/assets/patrick.png\",\"mainEntityOfPage\":{\"@id\":\"https://pyte.dev/posts/blocking-invalid-rcpt-postfix/\",\"@type\":\"WebPage\"},\"publisher\":{\"@type\":\"Organization\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://pyte.dev/assets/patrick.png\"},\"name\":\"Demians Blog\"}}"</script>
<script type="application/ld+json">"{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/\",\"name\":\"Demians Blog\",\"position\":1},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/\",\"name\":\"Posts\",\"position\":2},{\"@type\":\"ListItem\",\"item\":\"https://pyte.dev/posts/blocking-invalid-rcpt-postfix/\",\"name\":\"Blocking Invalid Recipients Before They Reach Your Exchange Server\",\"position\":3}]}"</script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800;900&family=Space+Grotesk:wght@400;500;600;700&display=swap"
rel="stylesheet">
<link
rel="stylesheet"
href="/css/theme.min.86a32b729f656fc6c119ed69193950db815a3ad9fdc967b32c76d602f11449a4.css"
integrity="sha256-hqMrcp9lb8bBGe1pGTlQ24FaOtn9yWezLHbWAvEUSaQ=">
<link
rel="stylesheet"
href="/css/syntax-dark.min.3e403a03e3af837b3829e9b6f01fc7792bda7cb7f5056f5e9786109545c6b2e1.css"
integrity="sha256-PkA6A&#43;Ovg3s4Kem28B/HeSvafLf1BW9el4YQlUXGsuE="
id="syntax-dark-theme"
class="syntax-theme"><link
rel="stylesheet"
href="/css/syntax-light.min.d0d33b879698595e6b2c0f75f0cea95a8517fb0150570cd0ee4dc42e25c8d147.css"
integrity="sha256-0NM7h5aYWV5rLA918M6pWoUX&#43;wFQVwzQ7k3ELiXI0Uc="
id="syntax-light-theme"
class="syntax-theme"
disabled><script>
(function () {
const storedTheme = localStorage.getItem("theme");
const systemPrefersLight = window.matchMedia("(prefers-color-scheme: light)").matches;
const theme = storedTheme || (systemPrefersLight ? "light" : "dark");
const syntaxDark = document.getElementById("syntax-dark-theme");
const syntaxLight = document.getElementById("syntax-light-theme");
if (theme === "light") {
if (syntaxDark) syntaxDark.disabled = true;
if (syntaxLight) syntaxLight.disabled = false;
} else {
if (syntaxDark) syntaxDark.disabled = false;
if (syntaxLight) syntaxLight.disabled = true;
}
const observer = new MutationObserver(() => {
const currentTheme = document.documentElement.getAttribute("data-theme");
if (currentTheme === "light") {
if (syntaxDark) syntaxDark.disabled = true;
if (syntaxLight) syntaxLight.disabled = false;
} else {
if (syntaxDark) syntaxDark.disabled = false;
if (syntaxLight) syntaxLight.disabled = true;
}
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["data-theme"],
});
})();
</script>
<link
rel="stylesheet"
href="/css/bundle.min.783a79746a859af9be598cbc33fba2a1087434b23768524277b07ef28336f113.css"
integrity="sha256-eDp5dGqFmvm&#43;WYy8M/uioQh0NLI3aFJCd7B&#43;8oM28RM=">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<link rel="icon" type="image/png" href="/favicon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/logo-transparent/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/logo-transparent/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/logo-transparent/apple-touch-icon.png">
<link
rel="icon"
type="image/png"
sizes="192x192"
href="/favicon/logo-transparent/android-chrome-192x192.png">
<link
rel="icon"
type="image/png"
sizes="512x512"
href="/favicon/logo-transparent/android-chrome-512x512.png">
<link rel="manifest" href="/favicon/logo-transparent/site.webmanifest">
</head>
<body class="flex flex-col min-h-screen">
<a href="#main-content" class="skip-to-main" aria-label="Skip to main content"
>Skip to main content</a
>
<header class="sticky-header">
<div class="header-container">
<nav class="header-nav" role="navigation" aria-label="Main navigation">
<div class="header-content">
<div class="header-logo">
<a href="/" class="logo-link" aria-label="Home - Demians Blog">
Demians Blog
</a>
</div>
<div class="header-menu">
<ul class="menu-list">
<li class="menu-item">
<a
href="/"
class="menu-link ">
Home
</a>
</li>
<li class="menu-item">
<a
href="/posts/"
class="menu-link ">
Blog
</a>
</li>
</ul>
<button id="search-toggle" class="search-toggle" aria-label="Search" type="button">
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
<button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" type="button">
<svg class="icon-sun" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
<svg class="icon-moon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
</svg>
</button>
</div>
</div>
</nav>
</div>
</header>
<main id="main-content" class="flex-1" role="main">
<div class="single-post-wrapper">
<article class="single-post">
<header class="post-header">
<h1 class="post-title-main">Blocking Invalid Recipients Before They Reach Your Exchange Server</h1>
<div class="post-meta">
<div class="post-meta-info">
<time datetime="2025-08-01T10:03:15&#43;02:00" class="post-date">
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
August 1, 2025
</time>
<span class="meta-separator"></span>
<span class="post-word-count">
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
762 words
</span>
<span class="meta-separator"></span>
<span class="post-reading-time">
<svg class="meta-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
4 min
</span>
</div>
</div>
</header>
<div class="post-content-main">
<p>Recently, I had to deal with a serious problem: <strong>backscatter</strong>.<br>
One of our mail gateways ended up listed on the <strong>backscatter.org</strong> blacklist for sending bounce messages to forged senders.</p>
<p>After checking the logs, I quickly realized that our system wasn&rsquo;t actually protected against backscatter attacks, so I had to do something about it.</p>
<h2 id="what-even-is-backscatter">What Even Is Backscatter?</h2>
<p>Backscatter is unwanted email that your mail server sends <strong>after</strong> receiving a message, usually in the form of a <strong>non-delivery report (NDR)</strong> or <strong>bounce</strong> to a <strong>forged sender address</strong>.</p>
<p>It happens when:</p>
<ul>
<li>A spammer sends email with a <strong>fake &ldquo;From&rdquo; address</strong> (often an innocent third party).</li>
<li>Your mail server <strong>accepts</strong> the message first, but later discovers its undeliverable.</li>
<li>Your server sends a <strong>bounce</strong> to the forged address, hitting an innocent person instead of the spammer.</li>
</ul>
<p>This makes your server appear to be sending spam, even though you&rsquo;re just bouncing bad mail.</p>
<h2 id="the-problem-in-our-setup">The Problem in Our Setup</h2>
<p>Our mail gateway mostly relays mail to multiple internal Exchange Server clusters.<br>
We have a list of valid domains configured, so Postfix will only accept mail for those domains.</p>
<p><strong>The problem?</strong><br>
Postfix didn&rsquo;t know which individual recipients were valid on the downstream Exchange servers.<br>
That meant it would happily accept messages for <strong>nonexistent users</strong>, only to later bounce them, classic backscatter behavior.</p>
<h2 id="the-goal">The Goal</h2>
<p>We needed to reject mail <strong>during the SMTP session</strong>, ideally right after the <code>RCPT TO</code> command, if the recipient didn&rsquo;t exist on the Exchange servers.</p>
<p>That way, the sending server would get the rejection immediately, and we would never have to generate a bounce message.</p>
<h2 id="possible-solutions-i-considered">Possible Solutions I Considered</h2>
<h3 id="1-relay_"><strong>1. relay_recipient_maps</strong></h3>
<p>This would require maintaining a <strong>full list of valid recipients</strong> in a Postfix lookup table.<br>
It works well, but means writing and maintaining a script to <strong>sync the list from Active Directory</strong> on a regular basis.</p>
<p>For us, this was too much custom scripting, too fragile, and too messy to maintain.</p>
<h3 id="2-virtual_"><strong>2. virtual_mailbox_maps with LDAP</strong></h3>
<p>Another approach would be using <strong>LDAP lookups</strong> directly against Active Directory to verify recipients in real-time.</p>
<p>This can work in some setups, but:</p>
<ul>
<li>It adds complexity and dependencies.</li>
<li>It can introduce security concerns.</li>
<li>It didnt fit well with our environment.</li>
</ul>
<p>So I ruled it out.</p>
<h2 id="the-solution-reject_unverified_recipient">The Solution: <code>reject_unverified_recipient</code></h2>
<p>While reading through the <strong>Postfix Address Verification Howto</strong>, I came across the <code>reject_unverified_recipient</code> option - bling! - exactly what I needed.</p>
<h3 id="how-it-works"><strong>How It Works</strong></h3>
<p>When an incoming SMTP session reaches the <code>RCPT TO</code> stage:</p>
<ol>
<li>Postfix checks if <code>reject_unverified_recipient</code> is enabled for the recipient domain.</li>
<li>If yes, it temporarily <strong>probes the downstream mail system</strong> to see if the recipient address exists.</li>
<li>If the downstream system says:
<ul>
<li><strong>User exists</strong> → Postfix continues processing.</li>
<li><strong>User does not exist</strong> → Postfix <strong>rejects immediately</strong> with:
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">550 5.1.1 &lt;user@example.de&gt;: Recipient address rejected: User unknown
</span></span></code></pre></div></li>
</ul>
</li>
</ol>
<p>Because the rejection happens <strong>during SMTP</strong>, no bounce is generated, and backscatter is avoided entirely.</p>
<h2 id="implementation-in-ispconfig">Implementation in ISPConfig</h2>
<p>In our case, the mail gateways run <strong>Postfix</strong> with <strong>ISPConfig</strong> as the management interface.<br>
I implemented a new configuration option in ISPConfig for <strong>per-domain control</strong> of <code>reject_unverified_recipient</code>, along with a <strong>validation server</strong> to specify for the downstream validation server.</p>
<h3 id="specifying-a-validation-server-for-recipient-verification">Specifying a Validation Server for Recipient Verification</h3>
<p>One important detail is that Exchange servers cannot validate recipients over the default SMTP transport on port <code>25</code>. To enable recipient validation, you need to activate it on the Exchange server and use the Hub Transport service, which by default runs on port <code>2525</code>.</p>
<p>Make sure to restrict access to this port, as it requires anonymous login specifically for recipient validation, you don&rsquo;t want that exposed broadly.</p>
<blockquote class="blockquote-regular">
<p><strong>Note:</strong> This setup does not affect your regular mail flow. Only the SMTP probes used for verifying recipients are sent to this validation server.<br>
Postfix enables this behavior with the <code>address_verify_transport_maps</code> option.</p>
</blockquote>
<p>To optimize performance and reduce unnecessary verification probes for addresses that have already been checked, I configured a local cache on the mail gateway using the <code>address_verify_map</code> option. This way, repeated probes for the same recipient are avoided.</p>
<p>Now, for domains that need recipient verification, we can enable it in the panel and point Postfix to the appropriate <strong>validation transport</strong>.</p>
<h2 id="the-result">The Result</h2>
<p>After enabling <code>reject_unverified_recipient</code> and pointing it at the Exchange clusters, the backscatter stopped completely.<br>
We were no longer accepting messages for invalid recipients.</p>
<h2 id="conclusion">Conclusion</h2>
<p>If your Postfix server is acting as a relay for Exchange (or any downstream mail system) and you&rsquo;re struggling with <strong>backscatter spam</strong>, enabling <code>reject_unverified_recipient</code> can be a clean and effective fix.</p>
<p>It avoids maintaining large static recipient maps, works dynamically, and ensures that invalid mail is rejected <strong>before</strong> it ever gets into your system.</p>
</div>
<nav class="post-navigation" aria-label="Post navigation">
<a
href="/posts/dovecot-index-cache-issues/"
class="nav-link nav-prev"
aria-label="Previous post: Dovecot Index Cache Issues">
<span class="nav-label">Previous</span>
<span class="nav-title">Dovecot Index Cache Issues</span>
</a>
<a
href="/posts/google-groups-spam/"
class="nav-link nav-next"
aria-label="Next post: Google Groups Spam">
<span class="nav-label">Next</span>
<span class="nav-title">Google Groups Spam</span>
</a>
</nav>
</article>
<aside class="post-toc" id="post-toc" aria-label="Table of contents">
<button
class="toc-toggle"
id="toc-toggle"
aria-expanded="true"
aria-controls="toc-content"
aria-label="Toggle table of contents">
<svg
class="toc-burger-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
<span class="toc-toggle-text">Table of Contents</span>
<svg
class="toc-chevron-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</button>
<div class="toc-content" id="toc-content">
<nav class="toc-nav" aria-label="Table of contents">
<nav id="TableOfContents">
<ul>
<li><a href="#what-even-is-backscatter">What Even Is Backscatter?</a></li>
<li><a href="#the-problem-in-our-setup">The Problem in Our Setup</a></li>
<li><a href="#the-goal">The Goal</a></li>
<li><a href="#possible-solutions-i-considered">Possible Solutions I Considered</a>
<ul>
<li><a href="#1-relay_"><strong>1. relay_recipient_maps</strong></a></li>
<li><a href="#2-virtual_"><strong>2. virtual_mailbox_maps with LDAP</strong></a></li>
</ul>
</li>
<li><a href="#the-solution-reject_unverified_recipient">The Solution: <code>reject_unverified_recipient</code></a>
<ul>
<li><a href="#how-it-works"><strong>How It Works</strong></a></li>
</ul>
</li>
<li><a href="#implementation-in-ispconfig">Implementation in ISPConfig</a>
<ul>
<li><a href="#specifying-a-validation-server-for-recipient-verification">Specifying a Validation Server for Recipient Verification</a></li>
</ul>
</li>
<li><a href="#the-result">The Result</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ul>
</nav>
</nav>
</div>
</aside>
</div>
</main>
<footer>
<footer class="site-footer">
<div class="footer-content">
<p class="footer-text">
&copy;
2026
Demians Blog.
Built with Hugo and Mana ❤️
</p>
<div class="footer-social">
<div class="social-links">
<a
href="https://github.com/pyte1"
target="_blank"
rel="noopener noreferrer"
class="social-link"
aria-label="GitHub">
<svg fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
fill-rule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clip-rule="evenodd"></path>
</svg>
</a>
<a href="mailto:demian%20%28at%29%20pyte%20%28dot%29%20dev" class="social-link" aria-label="Email">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path>
</svg>
</a>
</div>
</div>
</div>
</footer>
</footer>
<div id="search-modal" class="search-modal" aria-hidden="true" role="dialog" aria-label="Search">
<div class="search-modal-backdrop" id="search-modal-backdrop"></div>
<div class="search-modal-container">
<div class="search-input-wrapper">
<svg class="search-input-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
<input
type="search"
id="search-input"
class="search-input"
placeholder="Search..."
autocomplete="off"
aria-label="Search input">
<button class="search-input-clear" id="search-input-clear" aria-label="Clear search">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</button>
<button class="search-modal-close" id="search-modal-close" aria-label="Close search">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<div id="search-results" class="search-results"></div>
</div>
</div>
<button id="scroll-to-top" class="scroll-to-top" aria-label="Scroll to top" type="button">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
</svg>
</button>
<script src="/js/main.min.e60ab79dca7b920b4dc5cf3163ad5ce8794839b60f27778db65782f087be3e27.js" defer></script>
</body>
</html>