Route 53 vs Route 53 Resolver: What Each One Does

Logeshwaran.C

Route 53 marking a health check Unhealthy while your site opens perfectly in a browser almost never means your server is down — it means Route 53's health checkers are being blocked, mishandled, or misread in a way your browser never experiences. Here's the fact that reorganizes the whole problem: Route 53's HTTPS health checks don't even validate your SSL certificate — an expired or self-signed one can pass outright — while the same endpoint fails because a firewall, WAF, or load balancer that has never heard of Route 53's IP ranges quietly drops the request before your server sees it. Close the gap between what a health checker can reach and what a browser can reach, and the status flips green.

⚡ Quick Answer

Most common cause → your firewall, security group, or WAF isn't allowing Route 53's health-checker IP ranges, so the browser's request (from your own network) succeeds while the checker's request never lands.

Second most common → the health check's path/port returns something other than a 2xx or 3xx status code, or an HTTPS check is failing SNI/handshake even though your browser negotiates TLS fine.

If failover itself isn't switching → the health check probably isn't associated with the record at all — non-alias records need an explicit health check; alias records need Evaluate Target Health set to Yes.

Start with what a checker sees that a browser doesn't, then the fixes in order of how often each one is the actual cause.

Jake found this out the expensive way. He runs a small phone-and-repair shop, and he'd finally put his order-tracking page behind a Route 53 failover record — primary on his EC2-hosted app, secondary a static "we're taking orders by phone" page on S3, so a bad deploy wouldn't mean a blank page during his Diwali sale weekend. He tested it by unplugging his own laptop from the office Wi-Fi and loading the site on his phone's data connection: worked fine. Two weeks later his app crashed at 7 p.m. on a Saturday, the health check had been sitting on Unhealthy for six minutes, and the site was still serving a 502 to every customer, because the failover record was never wired to the health check in the first place. His browser had been telling him the truth about his own network. It had never once told him what Route 53 could see.

Ethan: "A Route 53 health checker isn't a person sitting somewhere with a browser open. Think of it as strangers around the world, each with a stopwatch and a plain-text request, none of them logged in, none of them running your JavaScript, none of them carrying a cookie from a previous visit. Your browser gets a warm welcome because it looks like a browser. A health checker gets whatever your infrastructure does to traffic that looks unfamiliar — and on a lot of setups, that's a WAF rule, a security group, or a load balancer quietly saying no before your application ever gets asked a question."

How Route 53 actually decides "healthy" or "unhealthy"

Route 53 runs health checkers from locations around the world, and they don't coordinate with each other. You can choose a request interval of 10 or 30 seconds, but because the checkers fire independently, your endpoint will sometimes see a burst of several requests in one second followed by a few seconds of silence, even though you asked for "every 30 seconds."

Each checker looks at two things: how fast your endpoint responds, and whether it fails a number of consecutive checks equal to your failure threshold (default three, adjustable one to ten). The timing budget differs by type. For HTTP and HTTPS checks, Route 53 must complete the TCP connection within four seconds, and your endpoint must return a 2xx or 3xx status code within two seconds after that. Plain TCP checks get ten seconds to connect. String-matching checks share the four-plus-two-second budget, then get two more seconds to receive and search the body.

Then comes the number that explains most "it went red for no reason" reports: Route 53 aggregates every checker's verdict, and if more than 18% of health checkers report an endpoint healthy, Route 53 calls it healthy — 18% or fewer, and it's unhealthy. That threshold exists so a network hiccup isolating one region's checkers doesn't flip your whole record on its own. It also means a firewall rule blocking every region equally will reliably push you under that line.

⚠️ What this actually breaks

HTTPS health checks do not validate your SSL/TLS certificate. An expired, self-signed, or mismatched certificate does not fail the check by itself, as long as the TCP connection and handshake complete and your endpoint answers with a 2xx or 3xx code. A health check can sit on "healthy" while every real visitor's browser shows a certificate warning. Don't use a green health check as proof your certificate is fine.

Why your browser succeeds and Route 53 still calls it Unhealthy

This is the diagnostic that actually matters, because "the site works for me" and "Route 53 thinks it's down" answer two different questions. Your browser is one device, on one network, that already trusts your certificate authority, sends the headers your app expects, runs your JavaScript, and is very possibly on an IP your firewall has already allowed for something else. A Route 53 health checker is none of those things.

What it does Your browser A Route 53 health checker
Source IPYour own network — often already allow-listedA published AWS range, unfamiliar to most firewalls by default
Runs JavaScript?Yes — renders the full pageNo — only the raw response body, first 5,120 bytes for string match
Certificate checkValidates and warns on expiry/mismatchDoes not validate the certificate at all
TLS SNIAlways sent automaticallyOnly sent if you turn on EnableSNI
Acceptable responseRenders whatever comes back, including 4xx/5xx pagesOnly 2xx or 3xx counts as healthy
GeographyOne locationMultiple AWS regions; needs 18%+ agreement to be healthy

🙋‍♂️ Jake's Reality Check

"I tested it on my phone with Wi-Fi off, so it wasn't even my office IP. Why would Route 53 see something different?"

Because your phone's carrier still isn't Route 53. The only way to see what a checker sees is to read the Health checkers tab in the console, covered further down. A second human device is still just a second human device.

Fix 1: open your firewall or security group to Route 53's health-checker ranges

This fixes the largest share of "unhealthy but works in browser" tickets, because "works in browser" and "works for Route 53" were never testing the same door. If your endpoint sits behind a security group, an on-premises firewall, or a router with its own allow list, the checker's requests need an explicit rule — nothing implicit lets them through just because your app is public.

  1. Get the current IP ranges. Route 53 publishes the exact list of health-checker IP addresses. Add both the current and older ranges — AWS specifically recommends including all the /26 and /18 CIDR blocks so you don't miss a checker that hasn't rotated onto the newest range yet.
  2. Prefer the managed prefix list over hand-typed CIDRs, if your endpoint is on EC2. Add an inbound security group rule allowing traffic from com.amazonaws.<region>.route53-healthchecks, substituting your resource's own region. AWS keeps that prefix list current automatically.
  3. Add the IPv6 variant if you check an IPv6 endpoint — a parallel prefix list, com.amazonaws.<region>.ipv6.route53-healthchecks, exists specifically for that.
  4. If you're not on EC2 — on-premises, a different cloud, a router with manual rules — paste in the published CIDR ranges directly, since there's no managed prefix list outside AWS's own networking layer.

One quiet trap: you might see requests arriving from a single IP in a region and be tempted to allow just that address. Don't — it can change to any other address in that region's range at any time, and yesterday's working rule fails silently tomorrow.

Fix 2: SNI and certificate handshake failures on HTTPS checks

Server Name Indication (SNI) is the piece of information a TLS client sends during the handshake — before any HTTP request — telling a server which hostname it's asking for. Every browser sends it automatically. Route 53 does not send it by default unless you turn on EnableSNI, and if your endpoint hosts several certificates on the same IP (normal behind a CDN, shared load balancer, or multi-tenant proxy), the server has no way to know which certificate to return without it.

The symptom is specific: the health check's failure reason reads SSL alert handshake_failure. Enable SNI first — it's set per health check, using the fully qualified domain name you've already configured — and if the failure persists, check whether your certificate's Common Name or Subject Alternative Names actually include that domain. A certificate issued only for www.example.com won't satisfy a check pointed at the bare example.com, or vice versa.

Fix 3: the path, status code, and string match Route 53 actually needs

Route 53 constructs its request in a specific way, and small mismatches here rarely show up when you open the same URL yourself. For an HTTP or HTTPS check on port 80 or 443, it sends the FQDN you configured as the Host header; on any other port, it sends domain:port instead — which matters if your app routes by Host header and you've checked it on a non-standard port without accounting for that.

The path needs to return 2xx or 3xx specifically when the endpoint is healthy — a check pointed at a URL that returns 401 because it requires a session, or 404 because it's a client-side route your framework handles after the page loads, will always read unhealthy no matter how fine the site actually is. Point the check at something genuinely public and server-rendered: a lightweight /health or /healthz endpoint returning a plain 200 is the standard, boring, correct answer.

If you're using string matching, remember Route 53 only searches the first 5,120 bytes of the response body, and the string must appear there in full — not injected later by client-side JavaScript. If the text you're matching is rendered by a single-page app after the initial HTML loads, the raw response Route 53 receives never contains it, and the check fails even though a human sees the text a second later.

Fix 4: WAF rules and bot protection blocking the checker

A health checker's request looks, structurally, like exactly the traffic a WAF, bot-management layer, or rate-based rule is built to catch: a plain request with none of a browser's usual headers or cookies, arriving in short bursts, from IP ranges spread across regions rather than one visitor's network. If your WAF has a rate-based rule, geographic restriction, or bot-control managed rule group in front of your origin, it can block the checker while every real visitor sails through.

The tell in the Health checkers tab looks different from a firewall problem: instead of a connection timeout, the failure reason shows a plain HTTP status code — typically 403 — and it shows up against nearly every checker in every region at once, rather than one region going quiet while the rest stay green. A firewall block usually reads as "can't connect"; a WAF block usually reads as "connected fine, got refused." The fix follows the same shape as the firewall fix: add an allow rule for Route 53's health-checker ranges ahead of your restrictive rules, so the checker is excluded from rate limiting and bot scoring before those rules get a chance to act. If your WAF sits in front of an ALB carrying an alias record with Evaluate Target Health on, this matters twice over, because a WAF block can make the target group look unhealthy even when every instance behind it is fine.

✅ Why this is the one to use

If none of the four fixes above match your logs, stop guessing and read the actual failure reason instead of the status color. Route 53 records why each checker failed — timeout, connection refused, SSL handshake, wrong status code, string not found — and that reason points straight at one of these four causes.

The health check is Unhealthy — but failover still isn't switching over

This is the failure Jake actually hit, and it's a different problem: the health check is reporting correctly, but Route 53 still answers with the primary during a real outage. Almost always, nothing ever told the failover record to care about the health check's result.

  1. Confirm both records share the same name, type, and Failover routing policy — one Primary, one Secondary. Route 53 can only fail over between records it recognizes as a matched pair.
  2. For a non-alias record (a plain A, AAAA, or CNAME), you must explicitly associate a health check with the primary. Skip this, and Route 53 treats the primary as always healthy regardless of what the check says.
  3. For an alias record (an ALB, NLB, CloudFront, API Gateway, or similar), set Evaluate Target Health to Yes instead — a separate health check is optional here, and it's more useful to let Route 53 read the resource's own health.
  4. Check whether both records are unhealthy at once. If both primary and secondary are unhealthy, Route 53 returns the primary anyway — a deliberate design choice, so a broken secondary won't be caught by this test alone.
Primary record type What drives failover Common mistake
Plain A/AAAA/CNAME (non-alias)An explicit health check, associated by IDHealth check exists but was never attached to the record
Alias to ALB or NLBEvaluate Target Health = Yes (inherits target group health)Also creating a redundant separate health check against the load balancer
Alias to S3 website endpointNo special health requirement — treated as effectively always healthyExpecting Route 53 to detect a broken S3 bucket automatically
Alias to CloudFrontEvaluate Target Health is not offered at allWaiting for a toggle that will never appear — see below

Special cases: ALB, NLB, S3, and CloudFront

Application and Network Load Balancers already run their own target-group health checks. Point an alias record at one with Evaluate Target Health on, and Route 53 reads that data directly — faster and more accurate than an external probe, since the load balancer sees individual unhealthy targets a Route 53 checker never could. A second, separate health check against the same load balancer's DNS name is redundant at best.

S3 website endpoints get health checks automatically as part of an alias record with Evaluate Target Health on — but S3 has no equivalent of "an instance going down," so there's effectively no mechanism for that alias to report unhealthy. If your failover plan depends on Route 53 noticing a broken S3-hosted secondary, that detection won't come from the S3 side.

CloudFront distributions are the case that catches people mid-setup: pick a CloudFront alias, and Evaluate Target Health is simply grayed out — unsupported, not broken. CloudFront doesn't expose the health signal Route 53 needs. If you need failover in front of CloudFront, either run a health check against the origin behind it on a non-alias record, or build failover into CloudFront's own custom error responses instead of relying on Route 53. If CloudFront and custom domains are new territory, the walkthrough on putting a domain and HTTPS in front of CloudFront covers the certificate and alias-record basics this section assumes.

Calculated and CloudWatch-based checks — and what to actually do about each

Two health check types skip the "send a request, time the response" model, and their unhealthy status needs a different read. A calculated health check monitors up to 255 other health checks and compares how many report healthy against a threshold you set. If a calculated check is unhealthy, don't look at its own settings — look at whichever child checks underneath it are actually failing, and fix those first.

A CloudWatch-metric health check watches the underlying CloudWatch data stream for an alarm, not the alarm's displayed state, so you can't force it healthy using CloudWatch's SetAlarmState API — that changes the alarm, not the stream Route 53 reads. The concrete next step: open CloudWatch → Alarms, find the exact alarm the health check references, and check whether it has recent data points at all. No data points usually means the metric source stopped publishing — a custom application no longer calling PutMetricData, or an EC2 detailed-monitoring gap — and that's a metrics problem to fix on your side, not evidence your endpoint is down. If data points exist and the alarm genuinely sits in Alarm, the fix is wherever that alarm's own threshold points, not in Route 53 at all.

How Route 53 works, and its routing policies, in plain terms

Route 53 is AWS's DNS service — it answers "which address does this name point to?" — and what makes it more than a plain DNS host is that the answer can change based on a routing policy rather than always returning the same value.

The policies are: Simple (one record, no health-based logic); Weighted (split traffic by a percentage); Latency-based (send visitors to whichever region answers fastest for them); Failover (the primary/secondary pattern this post is about); Geolocation and Geoproximity (route by where the visitor or resource is, with geoproximity adding a "bias" you can nudge to shift the boundary); Multivalue answer (return up to eight healthy answers, roughly at random); and IP-based routing (route by the requester's own IP or CIDR block). Health checks can attach to all of these except Simple, which has a single fixed value with nothing to branch on.

Verifying a health check without guessing

The console tells you exactly why a check is failing — most people just never open that tab.

  1. Sign in at console.aws.amazon.com/route53 and open Health checks in the navigation pane.
  2. Read the Status column first — a brand-new health check may say no status is available yet, because it has to propagate to Route 53's checkers before results appear. That's a genuine "wait a few minutes" case, unlike almost everything else in this post.
  3. Open the check's page and choose the Health checkers tab (every type except calculated checks). It lists each checker's IP, when it last checked, and either the current status or the specific reason for the last failure — the exact line to match against the fixes above.
  4. Cross-check with CloudWatch for the metric HealthCheckStatus and the percentage of checkers reporting healthy — useful for seeing whether you're hovering just under the 18% line or failing everywhere at once.

When nothing above fixes it — the honest last resort

Two situations genuinely can't be solved by adjusting the health check. First: Route 53 cannot check an endpoint whose IP is local, private, non-routable, or multicast — a hard constraint, not a misconfiguration. Second: if your security policy requires your origin to accept traffic only from a small, hand-approved allow list that will never include Route 53's ranges, a direct endpoint health check is the wrong tool for you.

In either case, switch to a CloudWatch-metric health check instead, driven by an alarm your own internal monitoring already raises — application error rate, a synthetic canary running inside your network, whatever tells you the truth today. Route 53 then fails over based on your alarm's data stream rather than trying to reach an endpoint it was never going to be allowed to reach. If your firewall truly cannot open, accept that and route around it — don't keep re-creating the same public health check and expecting a different result.

FAQ — Route 53 health checks and failover, answered straight

Why does Route 53 say my health check is unhealthy when the site loads fine in my browser?

Because a browser and a Route 53 health checker are not the same client. Your browser runs from your own network — often already trusted by your firewall — sends SNI automatically, validates certificates, and runs JavaScript. A health checker sends a plain request from a published AWS IP range, doesn't validate your certificate, needs SNI turned on explicitly, and only accepts a raw 2xx or 3xx status code. The gap is almost always a firewall, WAF, SNI, or response-code mismatch, not an actual outage.

What is a Route 53 health check?

A configuration that tells Route 53 to repeatedly send HTTP, HTTPS, or TCP requests to an endpoint (or monitor a CloudWatch alarm, or the combined status of other health checks) and report it healthy or unhealthy. You can then attach that result to DNS records so Route 53 only answers with healthy resources.

How do I fix a Route 53 health check that stays unhealthy?

Work through it in order of likelihood: allow Route 53's published health-checker IP ranges (or the managed prefix list) through your firewall, security group, and WAF; enable SNI on HTTPS checks if the failure reason mentions a handshake error; confirm the checked path returns a plain 2xx or 3xx status without requiring authentication; and if you're using string matching, confirm the text appears in the raw, un-rendered HTML within the first 5,120 bytes.

Why isn't Route 53 failover switching to my secondary record even though the health check shows unhealthy?

The health check almost certainly isn't associated with the record. Non-alias primary records need an explicit health check attached by ID; alias records (to an ALB, NLB, or similar) need Evaluate Target Health set to Yes instead. Without one of those two settings, Route 53 keeps returning the primary regardless of what the health check reports.

What IP addresses does Route 53 use to run health checks, and how do I allow them through my firewall?

Route 53 publishes the current list of health-checker IP ranges alongside its name servers and other service ranges. On EC2, the simplest approach is allowing the AWS-managed prefix list com.amazonaws.<region>.route53-healthchecks (and its IPv6 counterpart for IPv6 checks) in your security group, which AWS keeps updated automatically. Off AWS, you add the published CIDR ranges directly, including both the current and older /26 and /18 blocks.

Do I need to enable SNI for an HTTPS health check?

If your endpoint hosts more than one TLS certificate on the same IP — common behind a CDN, load balancer, or shared reverse proxy — yes. Without SNI enabled, the health check will typically fail with an SSL alert handshake_failure status because the server has no way to know which certificate to present.

What response code does Route 53 need to consider my endpoint healthy?

An HTTP status code of 2xx or 3xx, returned within two seconds after the TCP connection completes (which itself must complete within four seconds). Any other code — 4xx, 5xx, or a timeout — counts as a failed check.

Can Route 53 health-check a private or internal-only endpoint?

No. Route 53 cannot check the health of an endpoint whose IP address is local, private, non-routable, or multicast — this is a hard constraint. For internal resources, use a CloudWatch-metric health check driven by your own internal monitoring instead of a direct endpoint check.

What is Evaluate Target Health, and when do I use it instead of a health check?

It's a per-alias-record setting that tells Route 53 to read the health of the underlying AWS resource (like an ALB's target groups) directly, instead of — or alongside — a separate health check. For alias records pointing at AWS resources that support it, AWS recommends Evaluate Target Health over building a redundant external health check, since the resource's own health data is more accurate.

Why is Evaluate Target Health grayed out on my alias record?

Because the alias target doesn't support it — CloudFront distributions are the common case; CloudFront doesn't expose the health signal Route 53 needs. For CloudFront, run a health check against the origin behind it instead, or build failover into CloudFront's own custom error responses.

How does Route 53 actually work?

It's AWS's DNS service: it answers queries for a domain name with an IP address or another resource record. Unlike a plain DNS host, the answer can vary based on a routing policy you choose, health check results, visitor location, or a weighting you set, rather than always returning a fixed value.

What are Route 53's routing policies?

Simple, Weighted, Latency-based, Failover, Geolocation, Geoproximity, Multivalue answer, and IP-based routing. Health checks can attach to all of these except Simple, which returns a single fixed value with no health-based branching.

Where do I sign in to manage Route 53 health checks?

At console.aws.amazon.com/route53, using your AWS account credentials. Health checks live under the Health checks section in the navigation pane, separate from Hosted zones.

How much does a Route 53 health check cost?

A basic health check is $0.50 per month for an AWS endpoint or $0.75 for a non-AWS endpoint, prorated. Optional features — HTTPS, string matching, a fast 10-second interval, or latency measurement — each add $1.00 per month for an AWS endpoint or $2.00 for a non-AWS endpoint. New and existing customers get up to 50 health checks free on AWS endpoints within or linked to the same account, and health checks tied to Elastic Load Balancers or S3 website endpoints are provisioned automatically at no extra charge.

Why is my WAF blocking Route 53's health checks?

A health checker's request looks like exactly the kind of traffic a rate-based rule, geographic restriction, or bot-control rule is designed to catch — no browser headers, no cookies, arriving from AWS ranges across several regions. The failure reason usually shows a plain 403 rather than a timeout, and it hits nearly every checker in every region at once. Add an allow rule for Route 53's health-checker IP ranges ahead of your restrictive WAF rules.

What does "18% of health checkers" mean, and why did my health check flip to unhealthy briefly on its own?

Route 53 aggregates every individual checker's verdict; if more than 18% report the endpoint healthy, the overall status is healthy, and if 18% or fewer do, it's unhealthy. That threshold is deliberately low so a temporary network partition isolating one region's checkers doesn't flag your endpoint as down. A brief unexplained flip is often exactly that kind of transient regional partition self-correcting within a check or two — worth watching in CloudWatch rather than panicking over immediately.

Revision note. Written August 2026, covering Route 53's current health check types (HTTP, HTTPS, TCP, calculated, and CloudWatch-metric), the standard and fast request intervals, and the documented 18% health-checker quorum. This will need a second look if AWS changes that quorum value, as its own documentation notes it might, or extends Evaluate Target Health to resource types that don't have it today. If you're staring at a red Unhealthy badge right now and the site clearly works when you load it yourself — you're not imagining things, and you're not out of options; you're just looking at two different views of the same endpoint, and the fix is almost always smaller than it feels at 11 p.m.

Related