Aprimerose sells infrastructure to companies that have to prove where their data goes. On 17 October we looked at our own website’s traffic properly for the first time in a long while. Cloudflare’s seven-day view showed 230,680 requests. 200,170 of them were 301 redirects. 186,270 had no user agent. 185,560 were for a single hostname: app.aprimerose.com.
That hostname pointed at a Firebase app that no longer existed. Over HTTPS, Firebase answered “Site Not Found”. Over plain HTTP, it redirected to HTTPS. Cloudflare was set to reach origin servers over plain HTTP, so it fetched the HTTP version, got the redirect, and handed it to the visitor, who asked for the HTTPS address and got the same redirect again. The address redirected to itself. A client that follows redirects makes a chain of requests per attempt before it gives up (curl stopped at thirty), so one caller can look like many. The nine busiest client addresses, half of the week’s requests, belonged to Microsoft, and the top five countries (India, Australia, Korea, Hong Kong, Japan) match Azure regions. We never found out whose machines they were.
Deleting two DNS records fixed it. In a six-hour window the next morning, the hostname no longer appeared at all: about 1,300 requests to the zone, our own checks included, and 127 redirects, against a six-hour average of 8,240 and 7,150 the week before. A Sunday-morning window against a weekly average is a rough comparison. The hostname disappearing from the list is not.
The loop was the loud problem. Looking for its cause turned up six quieter ones.
1. Twenty-five DNS names for things we no longer use
Besides the dead app, the zone held hostnames for old products, a CNAME to an AWS load balancer that no longer existed (and the certificate-validation record that went with it), Skype for Business records for a service Microsoft shut down in 2021, SendGrid records whose targets no longer resolved, and three records with the company name misspelled. None of them was on any list. We deleted 25 names, the app among them.
Dead records are not only clutter. When a DNS name points at an account on another platform and that account is closed, someone else can sometimes claim it and serve content under your domain. That is a subdomain takeover, and it starts with exactly this kind of record.
2. Encryption was off where we assumed it was on
Cloudflare’s SSL/TLS setting said “Automatic”, which means Cloudflare crawls the origins and raises the encryption mode when every one of them passes. It never lowers it. The zone was on “Flexible”: encrypted between the visitor and Cloudflare, plain HTTP between Cloudflare and the origin. With the dead records in the zone the crawl never passed, so “Automatic” left it there. Flexible is what made the redirect loop possible, and it meant Cloudflare fetched from those origins unencrypted.
Once the records were gone, we set Full (strict) explicitly: Cloudflare now reaches the origin only over HTTPS and checks its certificate.
3. The page claiming GDPR alignment loaded Google on every visit
The homepage says “EU GDPR-aligned by design”. Every page loaded Google Analytics, which sets cookies, without a consent banner. Every page also loaded Google Fonts, which sends each visitor’s IP address to Google; a German court awarded damages over exactly that in 2022.
We keep a claims register that checks every statement on the site against evidence. It had “GDPR-aligned” marked as supported. The evidence was about the product: where client data is stored and how it is processed. Nobody had checked the page making the claim.
We removed Google Analytics, now serve the fonts from our own domain, and noted the gap in the register. Cloudflare’s own analytics stays: it sets no cookies, and Cloudflare already sees every request as the proxy, so it adds no new recipient. Whether a cookieless analytics script still needs a notice under EU ePrivacy rules is a legal question we haven’t closed.
4. The website answered 200 to everything
The site had no 404 page. Without one, Cloudflare Pages serves the homepage, with a 200 status, for any path that doesn’t exist. /wp-login.php returned the homepage. So did /robots.txt and /sitemap.xml, which meant that in practice we had neither.
We added a real 404 page, a robots.txt, a sitemap, and an HSTS header, which tells browsers to use HTTPS only. We also found that the deploy published every file in the site folder. That included a build configuration file. Its content was harmless, but nothing in the process would have stopped a secrets file from being published the same way. The repository now ignores the usual secrets files (.env, .dev.vars), so a careless git add can’t commit them. That guards against the common mistake; it doesn’t scan for secrets.
Six hours of real 404s the next morning showed why that matters. The most-requested path that didn’t exist was /.dev.vars, the local secrets file of Cloudflare’s developer tooling, with 40 requests, alongside the usual /.env variants. Scanners ask for exactly the kind of file our deploy could have published.
5. Two checks that could not see what they were checking
We run this work with an AI coding assistant, the same one drafting this post, and it ran the verification. Twice it reached the wrong conclusion from a check that could not see the thing it was checking.
First, the build configuration file. We deleted it, deployed, and checked: 404. Andrei could still open it. Every check had added a cache-busting parameter to the URL, to make sure we were looking at the new deployment. That also skipped the cache, and the cache held a copy that the plain address still served. Cloudflare’s documentation says a Pages file stays cached until the next deployment. This copy outlived three deployments, including the one that deleted the file, and a purge from the dashboard. It expires on its own after seven days, and the file is harmless, so we left it. We now check the plain address as well as the cache-busted one.
Second, analytics. The assistant fetched the homepage, found no Cloudflare analytics script, and reported that Cloudflare Web Analytics wasn’t installed. It had been running for four years. Cloudflare injects the script only when the request says it accepts HTML, and curl’s default Accept: */* doesn’t, so the page came back without it.
Neither mistake did any damage. Either could have gone into a status report as fact.
6. Anyone could send mail as us
This isn’t the website, but it lives in the same DNS zone. SPF ended in ?all, which tells receiving servers to treat unknown senders as neutral. DMARC was set to p=none with no reporting address, so it neither enforced anything nor told us anything. Mail from Google Workspace, our main sender, carried no DKIM signature in our own domain’s name. Anyone could send mail claiming to be from aprimerose.com, and receiving servers had no instruction to reject it.
Now Google signs our mail with a 2048-bit DKIM key in our domain’s name, SPF ends in ~all, and DMARC reports go to Cloudflare. A test message passes SPF, DKIM and DMARC. DNSSEC was off too, so a resolver that checks DNS signatures could not have verified our answers, the DKIM key included. It is on now.
DMARC itself is still at p=none while the first reports come in, so receiving servers still have no instruction to reject spoofed mail; the difference is that we now see it. Once a few days of reports show only our two senders, it moves to p=quarantine, which tells receivers to treat mail that fails the checks as suspect.
A second site, for comparison
The same week we ran the same checks on a small sports club’s website that had also just moved to Cloudflare. It passed all but one, because the migration was planned rather than inherited. The one gap was a known item still on its backlog: the domain receives no email and had no email records, so anyone could send mail in its name. Three records closed it that day: an SPF record allowing no senders, a DMARC record that rejects everything, and a “null MX” record declaring that the domain takes no mail.
Its most-requested path that week was /xmlrpc.php, WordPress’s remote API, which bots use to try passwords in bulk: 4,450 requests, against 1,220 for the homepage. The site had stopped being a WordPress site seven days earlier. The bots hadn’t noticed.
None of this needed a security product. Every problem was visible in a free dashboard or a one-line command, and every fix was a setting, a DNS record or a few files. What it took was looking, and then testing whether each check could actually see what it was checking.
We didn’t add firewall rules for the scanners. A static site has nothing for them to find, a block would only turn a 404 into a 403, and the rule would need maintaining. They get a 404 now, which is the correct answer.
The script in the record checks mail authentication (SPF, DKIM, DMARC), DNSSEC, the HTTP-to-HTTPS and www redirects, 404 behaviour on probe paths, the HSTS and frame headers, third-party hosts on the homepage, and the analytics script, each on the plain address and a cache-busted one where it matters.
The record behind this post (the analytics before and after, the evidence for each claim, and that script) is a field note, not a Chronos experiment, because none of this was registered in advance: field-notes/2026-10-18-website-hygiene.