Skip to content

What happens when your server inherits a pirate site's IP address

One morning a customer opened their dashboard and looked at the Hosts report, which had shown the same single row since the day they installed us. There were two rows now. The first was their domain. The second was a name they had never seen in their life.

They did what anyone would do and opened it.

It was their site. Not similar to their site. Theirs. Same layout, same copy, same photographs, same links, down to the wording of the footer. A different domain name at the top of the browser and nothing else different at all.

They came to us to work out what they were looking at.

The half hour we spent being wrong

Someone had scraped the site. That is where everybody lands and it is a reasonable place to land: cloning a site is cheap, it is common, and a stolen template with somebody else's text in it is worth real money to the right sort of person.

We were sure of it for about half an hour.

What didn't fit

A scraper takes a snapshot. Snapshots rot: a price changes, a blog post goes up, a nav item moves, and the copy stays where it was. This one didn't rot. It moved when the real site moved, immediately, because it was reading from the same place.

Then the detail that ended the theory. The pages on that unfamiliar domain were running the customer's own tracking script, and it was reporting home. That is why the hostname turned up in their dashboard at all.

It behaved like the same server because it was the same server.

Where the hostname actually comes from

Statable never asks you to declare your hostname. It does not look it up in DNS. The tracker reads location.href, the address sitting in the browser's own address bar, and sends it up with the pageview. The server parses the hostname out of that URL and stores it.

So the Hosts report is not configuration, it is a statement about where a page was standing when a real browser rendered it.

And Statable records that hostname whether or not it matches the domain on the account. There is no allowlist quietly dropping the ones that look wrong. That sounds like a small decision and it is the entire reason this story has an ending: a tool that only counted the domain you registered would have thrown the evidence away as noise.

The domain had a past

With scraping ruled out, we went looking into the history of the unfamiliar domain.

The Wayback Machine, historical DNS records, and a couple of other lookups told the full story. It had been a torrent tracker, the TV series kind. At some point the hosting provider terminated it over copyright complaints, and the domain went quiet.

And its old IP address went back into the provider's pool.

What actually happened

flowchart TD
    A["Torrent site runs on IP X"] -->|"terminated for copyright"| B["IP X returns to the provider's pool"]
    B -->|"reassigned later"| C["Customer's server now answers on IP X"]
    D["Old domain still resolves<br/>to IP X through Cloudflare"] --> C
    C -->|"nginx has no catch-all,<br/>so the first server block answers"| E["Old domain now serves<br/>the customer's site"]
    E -->|"customer's tracking script runs on the page"| F["Statable records<br/>an unfamiliar hostname"]

Nobody attacked anybody. Every link in that chain is a system doing exactly what it was told.

The provider recycled an address, which is what providers do with a finite resource. The old domain's DNS was never cleaned up, because nobody tidies the DNS of a site they have abandoned. Cloudflare kept proxying requests for that domain to the origin address its own customer had configured, probably years earlier, which is correct behaviour and not a failure of anything. The requests arrived at the address, found a new occupant, and the new occupant answered.

That last step is the only actual mistake in the chain.

Our customer runs nginx and had never configured a default server. That sounds harmless. It isn't, because nginx does not respond to an unrecognised Host header by refusing it. When no server_name matches, nginx falls back to the first server block listening on that port and serves whatever that block serves. There is always a default server. If you don't nominate one, nginx nominates one for you, and it will be your real site.

Cloudflare was in front of the dead domain with its own certificate for it, so visitors got a valid padlock over a perfectly ordinary looking website. Nothing about the page looked wrong, because nothing about it was broken. It was a real site, correctly served, under a name that belonged to somebody else.

It is tempting to file all this under freak coincidence, and it isn't one. Three of the four links are ordinary. Providers recycle addresses because IPv4 ran out years ago and they have no choice. Abandoned domains keep their old records for as long as somebody keeps paying the registration. And nginx without a nominated default server is not a misconfiguration you have to work at, it is simply what you get by leaving the file alone.

The only unusual ingredient here is the previous tenant, and the previous tenant is the part that changes nothing. Swap the torrent tracker for a folded consultancy and the mechanism is identical, minus the anecdote.

Why this is worse than being copied

A scraped copy is annoying. This is worse in ways that take a while to surface.

There are now two identical sites. Neither carries a canonical tag pointing at the other, because the customer had no idea the second one existed. Search engines do not index everything they crawl, and they will not keep two copies of the same site side by side: shown duplicates, they choose one address to hold on to and quietly drop the other. Nothing in that decision guarantees the original wins. The copy that was crawled first, or that happens to look better connected, can be the one that stays, and the site whose owner actually wrote the words is the one that disappears.

The domain brings its history and reputation with it. Years of links from warez forums and streaming aggregators, and whatever standing it had accumulated with search engines and blocklists before it was taken down. All of that is now attached to a domain serving a legitimate company's homepage.

The numbers were wrong the whole time. Every visit to the old domain landed in the customer's analytics, mixed in with their real traffic. Sessions, sources, conversion rates. Not enormously wrong, but wrong, and wrong in a way that no amount of staring at the dashboard would have explained.

And nothing about it is under your control. You do not own that domain. You cannot take it down, redirect it, or add a canonical tag to it. Whoever holds the registration can point it wherever they like, whenever they like, and you find out afterwards.

The fix takes four lines

The recycled IP address is not the bug. Addresses get recycled constantly, that is how a finite resource works, and you cannot opt out of it.

The bug is that the server answered.

Give nginx a catch-all and let it close the connection on anything it does not recognise:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    return 444;
}

Then list your real hostnames in their own blocks. The stale DNS record still points at you, the requests still arrive, and they leave empty handed.

Worth doing when you get a new address

If a server has just been given a fresh IP, spend five minutes on its history. The Wayback Machine and a historical DNS lookup will tell you who lived there before, and occasionally the answer is interesting.

When it isn't an accident

Our case was nobody's fault and four lines of nginx close it. The same row in the same report catches something those four lines will not touch.

There are several reasons to want a pixel-perfect copy of somebody else's business, and the good ones are all bad. The clearest is onboarding fraud: a payment provider reviewing a new merchant wants to see a real company with a real website behind it, and a convincing clone of an established business is a cheap way through a quick review. Phishing pages and fake storefronts want the same thing for the same reason. In every version of it, the point is to look exactly like you.

Copying a site carefully takes real work. Copying one quickly takes a command, and a quick copy takes everything, including the analytics snippet in your <head>. So the site built to impersonate you reports its own hostname to the company it is impersonating.

The nginx fix does nothing here, because a deliberate clone lives on the impersonator's own server and never touches your IP. You cannot stop someone copying HTML you publish to the world. What you get instead is a notification, and with impersonation the whole game is how early you find out.

The quietest report in the dashboard

We are not going to claim that hostname breakdowns are a Statable invention. Plenty of analytics tools can answer this question one way or another.

The difference is whether you ever see it.

Not every tool offers it at all, and the ones that do usually offer it as a dimension rather than a report: something you can slice by once you have thought to ask. And nobody thinks to ask this one, because the question is "is my website being served from somewhere I don't know about", and that is not a thought that visits you on a Tuesday afternoon.

We made it a tab. Hosts sits in the same row as Top Pages, Entry Pages and Exit Pages, at full size, on every site, whether it has one hostname or nine. Most of the time it is a single row and tells you nothing, which is exactly what a healthy site should look like. Our customer did not write a query, did not open a filter and did not suspect a thing. They read a report that was already on the screen, and it had grown a row.

That is the only part of this we would take credit for: a report you walk past every week beats a query you have to think of.

What to do today

Open your Hosts report and read the rows.

Subdomains you recognise are fine, that is what the report is for. A staging server that someone forgot to exclude is common and easy to fix. A name you do not recognise at all deserves an hour of your afternoon, and the first ten minutes of it should go to the Wayback Machine.

Then go and check what your web server does with a Host header it has never seen before. Most of them, out of the box, are more welcoming than you would like.


Ready to take control of your web analytics? Try Statable free for 30 days. No credit card required, full feature access, built for GDPR. Start your free trial or view a live demo.