Null: Eight Hundred Requests for a Page Nobody Ever Wrote

Every letter in this series so far has been about a claim that turned out to be false.

Letter B: a fleet claiming fifty-six identities. Letter D: our blocklist answering an adversary's questions honestly. Letter E: our own corpus claiming eighty-five hosts that never existed. Letter F: a third of all crawler traffic wearing a name it does not own.

This letter is about a claim nobody made.

There is a URL on this estate that has been requested 821 times over two months, by 70 distinct addresses. It has never existed. It has never been linked. It appears in no page, no sitemap, no database row, no structured-data field. We did not publish it, we have never published it, and the machine asking for it is not misreading anything we wrote.

The path is /threat-intel/null.

It also contains a second finding, which arrived by ambush. While writing this letter I nearly published a confident and completely false claim about who was responsible โ€” because our own enrichment data told me there were two culprits, and there is only one. That part is at the end, and it is the more useful half.

The shape of the thing

Requests for a null path821
Distinct addresses70
First seen2026-06-12
Last seen2026-08-09
Duration~2 months, continuous
Our answer, every time404 (0 exceptions)
Occurrences in our served HTML0
RequesterMeta, verified against published ranges

What they ask for

Two paths account for almost everything:

PathRequestsStatus
/threat-intel/null504404
/threat-intel/verify/null306404
/poezie/ghiolbani/null1404
/poezie/nu-stiu-cine-sunt/null1404
/share/null1404
/shorts/judecati/null1404
/shorts/let-me-be/null1404
/shorts/nothingness/null1404

The tail is the informative part. /threat-intel/ is where the dossiers live; /threat-intel/verify/ is where their attestation pages live; /poezie/ and /shorts/ are poetry and short prose, a different section of the site entirely, written for different readers and sharing no template with the dossiers.

The constant is not the section. It is the shape: <whatever directory the client was in>/null.

That is the fingerprint of a relative URL resolution. Something on the client's side produced the bare string null where a URL should have been, handed it to a browser engine, and the engine did what browser engines do with a bare relative string โ€” resolved it against the current directory and fetched it. On a dossier page that yields /threat-intel/null. On an attestation page, /threat-intel/verify/null. On a poem, /poezie/<slug>/null.

There is no configuration of our site that produces that pattern. There is exactly one configuration of a client that does.

What we actually serve

Because Letter E was written about believing a record without checking it, this was checked first and checked properly.

The referring pages are known: the Referer headers on the null requests point at individual dossiers โ€” ti-2026-026k-predecessor-network, ti-2026-027g, ti-2026-026b, ti-2026-026d, ti-2026-026c. So the question is narrow and answerable: does that page, as served, contain anything that could become null?

Taking ti-2026-026k-predecessor-network.html, 274,473 bytes, the page named in the largest single group of referers:

CheckResult
<script> blocks3
โ€” of which JSON-LD (data, not code)2
โ€” executable JavaScript1 block, 1,618 characters
.href = assignments0
location = assignments0
Literal null anywhere in the document0
Anchors with empty, missing or null href0

The one executable script is a dropdown filter. It opens a menu, it closes a menu, it assigns no URLs and fetches nothing.

The two JSON-LD blocks are Person and ClaimReview โ€” and JSON-LD is exactly what a link-preview scraper parses, so both were walked field by field looking for any value that was null, an empty string, or the literal text "null" or "undefined". Neither block contains one. Every field is populated.

An earlier internal investigation on 2026-08-07 reached the same conclusion by a different route: it enumerated 3,273 anchors across the referring pages and found none with a missing, empty or # href, and confirmed that published_dossiers holds zero null slugs across 821 rows. Two independent methods, same answer.

We are not emitting this. There is nothing on our side to fix.

Who is asking

All of it is Meta, and the verification is not a guess.

Requests carrying meta-externalagent/1.1820 of 821
The remaining onemeta-webindexer/1.1 โ€” also Meta
Agent confidence820 verified against Meta's published ranges, 1 unverifiable
Addresses69 in 57.141.0.0/16, plus 1 in legacy Meta space

The single outlier deserves its own line, because precision is the whole subject of this letter. One request came from 69.63.189.13 โ€” AS32934, Facebook, Inc., correctly attributed, but older Meta space rather than 57.141.x. It carries meta-webindexer/1.1 rather than meta-externalagent, and our classifier marks it unverifiable rather than verified: not because anything is suspicious, but because that particular product does not appear in the published range feeds, so there is nothing to check it against. It is Meta by every other signal and unprovable by the one that matters. One request in 821, and it still gets a caveat.

The user-agent string is worth quoting in full, because its shape matters later:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/145.0.0.0 Safari/537.36 (compatible; meta-externalagent/1.1
(+https://developers.facebook.com/docs/sharing/webmasters/crawler))

Meta's crawler declares itself by appending its token to a complete, ordinary Chrome user agent. The first ninety characters are indistinguishable from a person browsing on Windows. The disclosure is at the end.

That is honest behaviour โ€” Meta is telling you exactly what it is, and publishing the ranges that prove it โ€” and it is also the reason this crawler is easy to misread, as the last section of this letter demonstrates at my own expense.

The declared Chrome version, 145, is real and current, which is consistent with what this actually is: a headless Chromium doing link-preview rendering. It executes JavaScript. That is precisely the class of client that can fabricate a URL our static HTML never contained.

What the mechanism probably is

Confidence here drops, and the letter says so rather than guessing confidently โ€” which is the error Letter E was written about.

The pattern is consistent with a rendering client that reads a page, looks for a URL in some field it expects to be present, receives a JavaScript null or a JSON null, coerces it to a string, and passes "null" to a URL resolver that has no way to know the difference between the string "null" and a real relative path. new URL("null", "https://host/threat-intel/") resolves cleanly to https://host/threat-intel/null. No error is raised anywhere in that chain. Every component behaves correctly on its own terms.

The field being read is very likely a canonical URL, an Open Graph image, or a next/previous link โ€” something a preview generator wants and a page may legitimately not have. The candidate we can rule out is our structured data, which was checked field by field and is complete.

What can be said with high confidence: the string is produced client-side, after our HTML leaves the building, inside a renderer we cannot see. What cannot be said is which specific component does it, and this letter does not name one.

A 404 is not a retraction

The operationally interesting part is what happened after the first request.

We answered 404. We have answered 404 821 consecutive times over two months, with zero exceptions, which is the correct and only honest answer โ€” the resource does not exist and never did. The requests did not stop. The most recent one was 2026-08-09, still arriving, still 404, still from a client that has received nothing but 404s from that URL since June.

This is worth sitting beside Letter D. There we found that a 403 is a reply โ€” that refusing a request teaches the requester something, and the fleet in Letter B used our refusals to map which crawler names we would accept. A 404 is a reply too, and it says clearly: this does not exist. Two months of that message have changed nothing.

The reason is that nothing on the client side is listening. The 404 is delivered to a fetch whose result nobody reads, inside a pipeline whose job is generating a preview, which fails silently and moves on. The error handling that would notice the pattern would have to live in the layer that fabricated the URL, and that layer does not know it fabricated anything.

Which means: we cannot make this stop. There is no fix on our side because there is no defect on our side. We could special-case /null and return a 410 Gone, or a 200 with an empty document, and none of it would reach the code that needs to change. The only remedy available is the one already in place โ€” answer honestly, and count it.

What it costs

Almost nothing, and that is worth stating plainly rather than inflating.

821 requests over two months is roughly 13 a day, against a corpus of 285,662 requests in the same table. It is under 0.3% of traffic. No resource is consumed beyond a 404, no data leaks, no attack surface is exposed. A null path is a dead end by construction.

The cost is not load. It is epistemic.

For two months this estate's access log has contained a page that does not exist, requested by a legitimate, verified, well-behaved crawler, referred from pages that never linked to it. Anyone reading that log without this letter beside it would conclude that /threat-intel/null was once a real URL that has since been removed, and that Meta still holds the link. That conclusion would be entirely reasonable and entirely wrong.

Letter E documented a corpus containing eighty-five hosts that never existed, caused by our own ingest trusting a forgeable header. This is the same category of error from the opposite direction: a corpus containing a page that never existed, caused by somebody else's renderer inventing a string. In both cases the log is internally consistent, superficially plausible, and describing something unreal.

An access log is not a record of what you published. It is a record of what clients believed you published. Those are different documents, and only one of them is under your control.

The ambush: how this letter nearly said something false

Everything above is the letter I set out to write. What follows is the part I did not expect, and it is the reason the letter is worth reading twice.

Grouping the 821 requests by network produced this:

Network as our corpus reports itAddressesRequests
AS32934 โ€” Facebook, Inc.42482
AS16276 โ€” "RIPE Network Coordination Centre"28339

AS16276 is OVH, a French hosting provider with no relationship to Meta. Forty-one percent of the traffic appeared to come from an unrelated party. That is a far better story than "one company has a bug" โ€” two independent renderers, on unrelated networks, fabricating the identical URL against the same pages would imply a defect in a shared library used across the industry.

I had written that section. It was wrong.

The addresses in the supposed OVH block are 57.141.0.47, 57.141.0.5, 57.141.0.54, 57.141.20.1, 57.141.20.13. That is 57.141.0.0/16 โ€” Meta's own space, the same range as the AS32934 group. The agent classifier, which checks against Meta's published crawler ranges rather than against ASN lookups, marks every one of them verified / Meta-ExternalAgent. There is one requester, not two.

Our ASN enrichment is simply wrong for that range: it attributes part of Meta's 57.141.x space to AS16276 and labels the organisation "RIPE Network Coordination Centre", which is the regional registry, not an operator. The 2026-08-07 investigation had it right when it recorded that the null requests were "ALL in 57.141.20.0/24 (Meta AS32934)". My correction of it was the error.

The near-miss compounded. My first pass at the user agents grouped them truncated to 64 characters, which cut the string precisely where Meta's disclosure begins โ€” so 816 requests looked like ordinary Chrome browsers, and I wrote a paragraph about headless clients wearing browser identities. The full strings show meta-externalagent on 820 of 821. Two independent measurement mistakes, both pointing the same direction, each making the other look corroborated.

What caught it was mechanical rather than clever: re-running every headline figure against the database after the draft was written, rather than trusting the numbers that produced the draft. The count of Meta-declared requests came back 820 where the draft said 5, and that single contradiction unravelled both errors. A third pass then caught a remaining absolute โ€” the draft said all 70 addresses were in 57.141.0.0/16; 69 are, and one is not.

So this letter contains its own worked example. It is about a log that describes something unreal, and while writing it I nearly added two more unreal things to the record: a second culprit that does not exist, and a population of browsers that are not browsers.

Steelmanning the boring explanation

Did we publish a broken link and then fix it? No. The site is served from a versioned tree and the null path appears in no version of it; the earlier investigation additionally confirmed 0 null slugs across 821 published rows in the database that generates the pages. A removed link would not explain requests continuing to arrive two months later, nor the same pattern under /poezie/ and /shorts/, which share no template with the dossiers.

Is a third party linking to /threat-intel/null from their own site? The referer data refutes it. The referring pages are ours โ€” individual dossiers on www.shuffle-on.com. A client is on our page and, while on it, requests null relative to it. An external inbound link would show an external referer or none.

Is it a scanner probing for a null endpoint? No. Scanners guess at paths that might plausibly exist โ€” /admin, /.env, /wp-login.php โ€” and this estate sees thousands of those daily. null is not a plausible endpoint name, the requests carry legitimate referers from real pages, and they come from a crawler whose identity verifies against published ranges.

Is our web server rewriting something into null? Checked. The requests arrive with null already in the path; the server's only involvement is answering 404. Three different hosts are affected (www.shuffle-on.com 810, lsn.sonatural.life 10, lsn-photos.shuffle-on.com 1), served by different configurations, which rules out a single misconfigured vhost.

Could our own monitoring be generating these? No. All 70 addresses are external and verify as Meta; the ingest path now refuses non-routable sources outright, per Letter E.

What to take from it

Your logs contain other people's bugs. Not attacks, not misconfiguration on your side โ€” defects in software you have never installed, arriving as traffic you have to answer. The correct posture is to identify them, verify they are not yours, and then stop trying to fix them.

A 404 persuades nobody. Two months of the clearest possible "this does not exist" changed nothing, because the code that would act on it is not the code that reads the response. When the error handling lives on the other side of the network, correctness on your side is not a remedy.

ASN attribution is enrichment, not ground truth. Our corpus split one crawler across two networks and named a regional registry as the operator. An identity check against the vendor's published ranges got it right where the ASN lookup got it wrong โ€” the same lesson Letter F drew from AS15169 versus AS396982, arriving here from the opposite side. When those two sources disagree, believe the published ranges.

Truncation is a measurement decision. Cutting a user-agent string at 64 characters removed the only part that identified the client, and turned a well-behaved declared crawler into a population of anonymous browsers. The display width of a column is not a neutral choice; it decides what you can conclude.

Re-verify the numbers after you have written the argument, not before. The draft of this letter was internally consistent, well-sourced and wrong. What caught it was re-running each headline figure against the database at the end, when the conclusions were already committed to paper and a contradiction had somewhere to land.

Confidence: HIGH โ€” that the URL was never published by this estate. Multiple independent checks: zero occurrences in the served tree, zero null slugs across 821 published database rows, 3,273 anchors on the referring pages with no missing or empty href, and a field-by-field walk of both JSON-LD blocks on the primary referring page showing no null or empty value.

Confidence: HIGH โ€” that every request came from Meta and that there is only one requester. 820 of 821 carry meta-externalagent/1.1 from 69 addresses in 57.141.0.0/16, all verifying against Meta's published crawler ranges; the remaining request carries meta-webindexer/1.1 from 69.63.189.13 in AS32934 (Facebook, Inc.), which is Meta by attribution but unverifiable by published range because that product is not in the feeds.

Confidence: MODERATE โ€” on the specific mechanism. A client-side field expected to hold a URL, received as null, coerced to the string "null" and resolved relative to the current directory, accounts for every observation including the /poezie/ and /shorts/ variants. The responsible component is not identifiable from our side and is not named here.

Known data defect โ€” our ASN enrichment attributes part of Meta's 57.141.0.0/16 to AS16276 with organisation "RIPE Network Coordination Centre". The published-range check is authoritative and disagrees. Any analysis grouping this traffic by ASN will report two networks where there is one.

Verification. SELECT path, status_code, count(*) FROM requests WHERE path LIKE '%/null%' GROUP BY 1,2 for the request pattern; SELECT user_agent, count(*) without truncation for the identity; SELECT referer, count(*) for the referring pages. Confirm the site side with a recursive grep for null across the served tree and a JSON parse of every application/ld+json block on any referring page. Do not group by ASN without cross-checking against the vendor's published ranges.

โš  Personal capacity. Research published independently โ€” not reflecting employer views. Derived from passive observation of attacks against personal infrastructure. Full disclaimer โ†’
โ† Previous The Census โ€” 7 / 14 Next โ†’