<?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: Web-Engineer</title>
    <description>The latest articles on DEV Community by Web-Engineer (web-engine).</description>
    <link>https://dev.to/web-engine</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%2Forganization%2Fprofile_image%2F15308%2Fbe5eedc7-e176-4578-aea9-b10a5e0c9db5.png</url>
      <title>DEV Community: Web-Engineer</title>
      <link>https://dev.to/web-engine</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/web-engine"/>
    <language>en</language>
    <item>
      <title>Passwordless sign-in for DontSendMeACard, powered by Web-Engine Sentry</title>
      <dc:creator>Craig Lawson</dc:creator>
      <pubDate>Fri, 30 Oct 2026 17:05:56 +0000</pubDate>
      <link>https://dev.to/web-engine/passwordless-sign-in-for-dontsendmeacard-powered-by-web-engine-sentry-4j3g</link>
      <guid>https://dev.to/web-engine/passwordless-sign-in-for-dontsendmeacard-powered-by-web-engine-sentry-4j3g</guid>
      <description>&lt;p&gt;Signing in to DontSendMeACard.com now takes one field. You type your email address, a code lands in your inbox, you enter it, and you're in. There's no password to remember and no traffic lights to click. Behind that simple screen is a change we've wanted to make for a while: using Web-Engine Sentry, our own bot-protection service, to stop spam &lt;em&gt;and&lt;/em&gt; make sign-in faster for real people.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of background
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xt60jir4uxy5nq7gsyx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9xt60jir4uxy5nq7gsyx.png" width="800" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.dontsendmeacard.com/" rel="noopener noreferrer"&gt;DontSendMeACard.com&lt;/a&gt; describes itself as "the online alternative to greeting cards". People send e-cards, often designed by charities, and donate what they would have spent on paper cards and postage. We've supported the platform for years. Like any site with public sign-up forms, it attracts bots.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the captcha wasn't the problem, and wasn't the answer
&lt;/h2&gt;

&lt;p&gt;A few years ago the site's sign-up form started filling with junk accounts, even though Google reCAPTCHA v2 was in place. When we audited the history recently, the numbers were stark. Between September 2023 and August 2024, with reCAPTCHA v2 on the form, sign-ups matching our spam pattern ran at roughly 200 to nearly 1,000 a month.&lt;/p&gt;

&lt;p&gt;The uncomfortable finding was that the server was checking reCAPTCHA correctly. The bots were simply arriving with tokens that passed. That's an important lesson. A captcha integration can be implemented perfectly and still fail, because the challenge itself is no longer a reliable test of whether a visitor is human.&lt;/p&gt;

&lt;p&gt;A stop-gap was added in 2024 and the spam dropped sharply. But that check was never designed to prove a visitor was a person, and charities told us it wasn't the friendliest way to register. It needed replacing with something built for the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step one: Sentry on every front door
&lt;/h2&gt;

&lt;p&gt;We started by switching the site's three public entry points to Sentry: charity sign-up, donor account set-up and sign-in. One setting controls all three.&lt;/p&gt;

&lt;p&gt;A few design choices matter here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server-to-server confirmation.&lt;/strong&gt; The browser widget's result isn't trusted on its own. The site's server confirms each result directly with Sentry before it creates an account or signs anyone in. A missing, forged or reused result is refused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choice of challenge.&lt;/strong&gt; Visitors aren't forced into one kind of test. For example, they can solve a visual puzzle or verify with an emailed code. Email verification needs no puzzle and doesn't depend on being able to see one, which makes it a more accessible route for many people.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience.&lt;/strong&gt; The widget loads from a backup CDN if the primary one is unavailable, and confirmation falls back to a second API host. We've written about &lt;a href="https://www.web-engineer.co.uk/blog/failover-beyond-dns" rel="noopener noreferrer"&gt;how we built failover into Sentry&lt;/a&gt; before, so we won't repeat it here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before release we tested the obvious attacks locally: posting with no result, a forged result, a result replayed a second time and a result that claims to come from the wrong host. Each one was refused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step two: turning the human check into the sign-in
&lt;/h2&gt;

&lt;p&gt;Once Sentry was verifying email addresses on the sign-in page, a question followed naturally. If we've already proved someone controls an inbox, why also ask for a password?&lt;/p&gt;

&lt;p&gt;So the next day we flipped the sign-in page around:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enter your email.&lt;/strong&gt; That's the only field.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sentry emails you a one-time code.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enter the code.&lt;/strong&gt; The form submits as soon as Sentry accepts it. There's no extra "Sign in" button to hunt for.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You're signed in to the account whose address Sentry verified. The address typed into the page is never trusted on its own. If someone verifies one address and then tries to submit another, the sign-in is refused. Other safeguards follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Codes are &lt;strong&gt;single-use&lt;/strong&gt; and &lt;strong&gt;time-limited&lt;/strong&gt;. A code is accepted for 15 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every sign-in starts a fresh session&lt;/strong&gt;, whichever route you used.&lt;/li&gt;
&lt;li&gt;Matching is &lt;strong&gt;exact&lt;/strong&gt;, so addresses that only look similar can't be swapped in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Passwords haven't disappeared. A small &lt;strong&gt;"Try another way"&lt;/strong&gt; link under the form leads to the familiar email-and-password sign-in and password reset, for anyone who prefers them. The new sign-in also sits behind a configuration switch, so it can be turned off without a deploy if it ever needs to be.&lt;/p&gt;

&lt;p&gt;Here's the shape of the server-side check, as &lt;strong&gt;illustrative pseudo-code only&lt;/strong&gt;. It isn't Sentry's API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Illustrative only: sign in as the address that was verified,&lt;/span&gt;
&lt;span class="c1"&gt;// never the address the form claims.&lt;/span&gt;
&lt;span class="nv"&gt;$receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;confirmWithVerificationService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// server to server&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$receipt&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$receipt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$receipt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;refuse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findOpenAccountByExactEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$receipt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;verifiedEmail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;offerRegistrationOrTryAnotherWay&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;startFreshSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line is the lookup. The account comes from what was &lt;em&gt;verified&lt;/em&gt;, not from what was &lt;em&gt;typed&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Codes that look like they belong
&lt;/h2&gt;

&lt;p&gt;A one-time code is only reassuring if people trust the email it arrives in. A message from an unfamiliar sender with someone else's branding looks a lot like phishing.&lt;/p&gt;

&lt;p&gt;DontSendMeACard is the first site to use a new Sentry option for its own mail settings and branding. The code emails now arrive under the DontSendMeACard name, with its logo and its own wording, and that can be updated from our cloud platform in moments, without touching the site's code. The DSMAC team are already refining the copy, including a clear reminder that staff will never ask you for your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it adds up to
&lt;/h2&gt;

&lt;p&gt;For visitors, sign-in is now one field and one code. For the site, every way in passes through a check that's confirmed on the server rather than trusted from the browser. And because email verification needs no puzzle, that check is easier for more people to complete.&lt;/p&gt;

&lt;p&gt;We'll be honest about one thing: it's early. The change has only just gone live, and we'll review the sign-up spam figures over the coming weeks. Moving from reCAPTCHA to Sentry was a deliberate choice, made because the data showed reCAPTCHA wasn't stopping these bots. We'll share how it holds up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want the same for your site?
&lt;/h2&gt;

&lt;p&gt;Web-Engine Sentry is WordPress-first, but it ships with an API and examples, which is exactly how we integrated it into DontSendMeACard's custom PHP platform. If spam is getting through your forms, or your sign-in is more effort than it needs to be, &lt;a href="https://www.web-engineer.co.uk/contact" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt;. We're happy to talk through what would work for you.&lt;/p&gt;

</description>
      <category>spamprotection</category>
      <category>webenginesentry</category>
      <category>casestudy</category>
      <category>passwordless</category>
    </item>
    <item>
      <title>Failover beyond DNS</title>
      <dc:creator>Craig Lawson</dc:creator>
      <pubDate>Thu, 15 Oct 2026 15:11:32 +0000</pubDate>
      <link>https://dev.to/web-engine/failover-beyond-dns-1e36</link>
      <guid>https://dev.to/web-engine/failover-beyond-dns-1e36</guid>
      <description>&lt;p&gt;So you've built a service and it's running in the cloud. It sits in a Kubernetes cluster, it's designed for high availability, and every component has a replica. Then the data centre loses its connection to the internet, and none of that helps.&lt;/p&gt;

&lt;p&gt;It happens more often than we'd like to admit. Data centres with multiple independent points of presence still have outages. On 20 October 2025, a DNS fault inside AWS's US-EAST-1 region cascaded into hours of disruption for a long list of services that had nothing to do with each other, apart from where they were hosted. Cloudflare has had more than one bad day too, including an outage of its 1.1.1.1 DNS resolver in July 2025 and a network-wide failure that November. When a large share of the web sits on a handful of providers, one provider's bad afternoon becomes everyone's.&lt;/p&gt;

&lt;p&gt;The uncomfortable lesson is that your hardware can be perfectly healthy and you can still be offline. Spread yourself across data centres with failover between them, and a DNS problem can still undo the whole arrangement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this mattered to us
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.web-engineer.co.uk/work/we-sentry" rel="noopener noreferrer"&gt;Web-Engine Sentry&lt;/a&gt; (Sentry for short) is our site-protection plugin. It cuts spam by making a site hard work for bots while keeping it easy for humans. It's built for &lt;a href="https://wordpress.org/plugins/web-engine-sentry/" rel="noopener noreferrer"&gt;WordPress&lt;/a&gt; first, and it ships with an &lt;a href="https://www.web-engineer.co.uk/doc/sentry-api" rel="noopener noreferrer"&gt;API&lt;/a&gt; and examples so it can protect any site. Sentry gives sites several ways to check that a visitor is real: captcha, email verification and mobile verification. As soon as a site puts one of those checks in front of its contact form or login page, our uptime becomes their uptime. If we're unreachable, their visitors can't get in touch or sign in.&lt;/p&gt;

&lt;p&gt;So "it's in the cloud" wasn't a good enough answer. We needed a plan that keeps the service running wherever possible, whatever fails underneath it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option one: throw hardware at it
&lt;/h2&gt;

&lt;p&gt;You can solve most availability problems with hardware, if you have the budget. Large cloud providers do exactly that, and they price it in. For most teams, though, a purely hardware-based answer is expensive, and you're still bound by the physical limits of someone else's estate.&lt;/p&gt;

&lt;p&gt;The conventional approach would be to host in two geographic regions and use DNS failover to switch between them. It works, but it isn't instant. Once the primary goes down, health checks have to notice, records have to change, and resolvers around the world have to let go of cached answers. Short TTLs help, but not every resolver honours them. That leaves a window, often a few minutes, where requests fail. For a verification step sitting in front of someone's checkout, a few minutes is a long time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option two: make the software smarter
&lt;/h2&gt;

&lt;p&gt;The alternative is to let the client decide. Give the consumer of the service an ordered list of endpoints, add some basic error handling, and when a request fails, move to the next one straight away.&lt;/p&gt;

&lt;p&gt;The real advantage is that those endpoints can be completely independent: different hosting, different networks, even different DNS providers. Failover no longer waits for a record to propagate. It happens on the next request, in milliseconds. Our weak point becomes our own code and the service itself, rather than the rigid physical constraints of our suppliers, and our own code is something we can test, improve and ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DNS failover vs client-side fallback&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faac7q1a9nn9f6si2r75c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faac7q1a9nn9f6si2r75c.png" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea in its simplest form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified illustration, not the Sentry SDK itself&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ENDPOINTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://a.example-primary.net&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://b.example-secondary.org&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://c.example-tertiary.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;callService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;ENDPOINTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// network error or timeout: try the next endpoint&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;All endpoints unavailable&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Decentralise by design
&lt;/h2&gt;

&lt;p&gt;Real resilience comes from breaking the application into its key parts and giving each one its own availability strategy. Once you treat each component as a separate service, you can tune how each one fails over. Here's how that played out for Sentry.&lt;/p&gt;

&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;We spread our endpoints across several domains, each managed by a different DNS provider, with multiple endpoints behind each domain. If one provider has a bad day, the others are unaffected. Relatively short TTLs mean we can repoint routes quickly when we need to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;The browser script loads from whichever CDN endpoint is available. The frontend knows about the alternatives, so if a script fails to load it moves on to the next one. That gives us geographically separate fallbacks managed entirely on the visitor's side. Once it finds an endpoint that works, it sticks with it for the rest of the session, so visitors don't pay the cost of a failed attempt twice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;On the site's own server, the Sentry SDK acts as a gateway to the available service endpoints. That pushed us to treat each backend as a separate node from the start and to design a proper synchronisation strategy between them. We had to decide early what the service provides and how to deliver it in a decentralised way, rather than bolting resilience on later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data
&lt;/h3&gt;

&lt;p&gt;Each node also runs its own separate database, holding a specific set of tables duplicated across several sites. Our APIs keep these in sync with the master. Some of that data is read-only reference data; the rest lets a node keep tracking activity on its own. That's enough for the service to keep running even when the primary site is unavailable. When the master comes back, each node syncs what it recorded back to it.&lt;/p&gt;

&lt;p&gt;Behind everything sits a highly available database cluster, and the nodes treat it as the ultimate source of truth. On paper, that cluster should never go down. We deliberately don't assume it won't. Making no promises about its availability gives us another layer of fault tolerance, and forced us to plan up front how data is reconciled once everything is back online.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;None of this is exotic. It's a fallback list, some careful error handling, and the discipline to assume every layer can fail. Together those give a site running Sentry verification that keeps working through regional outages, DNS failures and the occasional very bad day in a data centre.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4aukbr6dh7f0m91p8f1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4aukbr6dh7f0m91p8f1q.png" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it, and tell us what you think
&lt;/h2&gt;

&lt;p&gt;You can install Sentry from the &lt;a href="https://wordpress.org/plugins/web-engine-sentry/" rel="noopener noreferrer"&gt;WordPress plugin directory&lt;/a&gt;, or read more about &lt;a href="https://www.web-engineer.co.uk/work/we-sentry" rel="noopener noreferrer"&gt;Web-Engine Sentry&lt;/a&gt; and the &lt;a href="https://www.web-engineer.co.uk/doc/sentry-api" rel="noopener noreferrer"&gt;Sentry API&lt;/a&gt; to use it on any site.&lt;/p&gt;

&lt;p&gt;How do you handle failover in your own services? Have you been caught out by DNS? Join the discussion and let us know.&lt;/p&gt;

</description>
      <category>sentry</category>
      <category>highavailability</category>
      <category>failover</category>
      <category>dns</category>
    </item>
  </channel>
</rss>
