Back to Posts

    The Site That Kept Reverting to a Parked Page (And Why It Wasn't the App)

    Posted June 22, 2026Updated June 22, 2026

    A company site went live on deploy, then silently reverted to a hosting parked page roughly 14 hours later — every time. The fix had nothing to do with the application. A debugging story about chasing the wrong theories before dig told the truth.

    AstroNode.jsHostingerLiteSpeedAWS Route 53DNS
    // Impact: Root-caused a recurring silent outage to a registrar-vs-DNS-host mismatch; fixed with a one-record DNS change, no app changes, no site rebuild

    The Site That Kept Reverting to a Parked Page (And Why It Wasn't the App)

    TLDR

    Our draft site kept reverting to a hosting "parked domain" page about 14 hours after every deploy. I burned time on the obvious suspects — a crashed app, an idle timeout, hourly process recycling — and every one of them turned out to be wrong. The real cause: the domain is registered at Hostinger but its DNS lives on AWS Route 53, and Route 53 still had a stale CNAME pointing the host at Hostinger's shared CDN edge. That edge had no live mapping for our hostname, so it served the parked page while our app sat there, perfectly healthy, answering on its origin IP. One DNS record swap fixed it. No app changes. No site rebuild.

    The Symptom

    The setup was an Astro SSR app on Hostinger's managed Node.js. Deploy it, and draft-website.mycoolapp.dev would come up fine. Then, reliably, somewhere around the 14-hour mark, it would quietly fall back to the "Parked Domain name on Hostinger DNS system" placeholder. Restarting the app didn't help. Redeploying didn't help. The only thing that brought it back was deleting the entire website in the panel and recreating it from scratch — which would buy another ~14 hours before the whole thing repeated.

    That "only a full recreate fixes it" detail nagged at me the entire time, and in hindsight it was the biggest clue. But I didn't read it right at first.

    The Theories I Was Sure About (And Was Wrong About)

    This is the part worth sharing, because the wrong turns are where the actual learning is.

    Theory 1: the app crashed. Easy to test, easy to kill. I SSH'd in and hit the app directly on the server's loopback, bypassing everything in front of it:

    curl -k --resolve draft-website.mycoolapp.dev:443:127.0.0.1 \
      https://draft-website.mycoolapp.dev/
    

    It returned the real site, HTTP 200, with a valid cert. The app wasn't down. It cold-started on demand and served correctly. So whatever was showing the parked page, it wasn't because the application had died.

    Theory 2: an idle timeout — just keep it warm. This felt right. Managed Node on shared hosting, no traffic overnight, process gets reaped, fallback page shows. The classic fix is an uptime monitor pinging the site every couple of minutes. Except… the site owner already had an Uptime Kuma monitor hitting it every 120 seconds with a keyword check, and it still reverted. A request every two minutes is far more than enough to keep any idle process alive. Theory dead.

    (A useful aside from that monitor: the parked page returns HTTP 200. If your check only looks at the status code, it will happily report your site as UP while it's serving a placeholder. The keyword check is what actually caught the revert.)

    Theory 3: the hourly process recycle gap. The server logs showed the Node worker restarting on an eerily exact cadence — every 3720 seconds, to the second, over and over. That's a fixed LiteSpeed lifetime timer recycling the process roughly once an hour. My theory: the CDN edge probes during the brief restart gap, gets a bad response, and latches the host to "parked." It even lined up with one timestamp. Then the owner pointed out the inconvenient fact: the first two reverts happened overnight, with no monitoring and effectively no traffic. No requests means nothing to land in a gap. That theory died too — and the hourly recycle, it turns out, is completely normal and harmless behaviour for Node on shared LiteSpeed.

    Three confident theories, three falsifications. Each one cost time, but each one also removed a whole category of cause. By elimination I was being pushed somewhere I hadn't looked: not the app, not the process, not the traffic. The network path in front of it.

    What dig Actually Said

    When the panel and your assumptions disagree with reality, stop arguing with the panel and ask the nameservers directly.

    dig +short NS mycoolapp.dev
    # ns-1326.awsdns-37.org.
    # ns-1919.awsdns-47.co.uk.
    # ...
    
    dig +short draft-website.mycoolapp.dev
    # free.cdn.hstgr.net.   <-- a CNAME to Hostinger's SHARED edge
    

    There it was. Two facts that didn't fit the story I'd been told:

    1. The domain's nameservers are on AWS Route 53, not Hostinger — even though the domain was bought at Hostinger.
    2. The live record for the host was a CNAME to free.cdn.hstgr.net, Hostinger's shared CDN edge.

    Meanwhile, over in Hostinger's own DNS panel, there was a perfectly correct zone for this host — an A record pointing straight at the origin server. It just wasn't doing anything, because nobody's resolver was reading Hostinger's panel. The world reads the nameservers, and the nameservers were AWS.

    So here's the actual mechanism. The shared edge routes by hostname. When you create the website, Hostinger provisions a mapping on that edge ("this hostname → that app") and it works. But that mapping isn't durable for a domain it can't verify through its own DNS, and after a grace window the edge drops it and falls back to the parked page. The origin never changed. The app never changed. The edge simply stopped pointing at it — and because our DNS sent everyone to the shared edge instead of to the origin, everyone got the placeholder.

    That also finally explained the recreate-only fix: recreating the site re-provisioned a fresh edge mapping, good for another ~14 hours, while the real problem — the DNS pointing at the wrong place — sat untouched the whole time.

    The Mental Model I Should Have Started With

    Your registrar and your DNS host do not have to be the same company, and only the nameservers decide what the internet actually sees.

    Buying a domain somewhere does not mean that company controls its DNS. If you delegate nameservers elsewhere — AWS, Cloudflare, wherever — then that is where records take effect, and the original registrar's DNS editor becomes a decorative dead-end that will happily show you records nobody is serving. That gap between "what the panel shows" and "what dig returns" is exactly where this bug lived.

    The Fix

    In Route 53, replace the stale shared-edge CNAME with the origin records Hostinger actually intended (an A and an AAAA straight to the origin). Because DNS won't let a CNAME coexist with other record types on the same name, this has to be one atomic change — delete the CNAME and create the A in a single batch — which Route 53 handles fine.

    Then verify, in layers, instead of trusting a single green checkmark:

    # authoritative truth, straight from the nameserver
    NS=$(dig +short NS mycoolapp.dev | head -1)
    dig +short @"$NS" draft-website.mycoolapp.dev A
    
    # what public resolvers are returning yet
    dig +short @1.1.1.1 draft-website.mycoolapp.dev
    
    # the real thing, end to end, checking the cert verifies
    curl -sS -o /dev/null \
      -w "HTTP %{http_code}  cert=%{ssl_verify_result}  ip=%{remote_ip}\n" \
      https://draft-website.mycoolapp.dev/
    

    HTTP 200, cert=0, resolving to the origin IP. No website recreate. No app touched. The thing that was "broken" for days was one wrong record in the zone that actually mattered.

    What I'm Taking Away From This

    • Falsify theories with evidence, fast. Each wrong guess was cheap to disprove and each one narrowed the search. The mistake isn't having a wrong theory; it's clinging to one without testing it.
    • A 200 is not an "up." A placeholder, a parked page, a login wall — all can return 200. Monitor for content you expect (a keyword), not just a status code.
    • Bypass the edge to test the origin. curl --resolve host:443:origin-ip is the single most clarifying command when something in front of your app might be lying. Separate "is the app healthy" from "is the path to it healthy."
    • Trust dig, not the dashboard. When a control panel insists everything is configured correctly and reality disagrees, ask the authoritative nameserver what it's actually serving.
    • Read your numbers quantitatively. A restart interval that's constant to the second is a timer, not load. The data was telling me "this is scheduled, not reactive" long before I listened.

    The unglamorous truth is that the most stubborn outages often aren't in your code at all. They're in the quiet seams between systems that each think the other is in charge.

    // © 2026 Chisom Onuegbu