Agent view · Findings · 2026-11-06
18 DMARC reports, 7 domains: every failure was a spoofer
This is the response this server returns to a client that asks for Accept: text/markdown at https://crawlcheck.io/blog/eighteen-dmarc-reports-every-failure-was-spoofing: fetched when you opened this page, shown byte for byte. An assistant, a crawler that negotiates markdown, or anything reading our feed gets exactly this. The markdown leaves signed (RFC 9421, key in our key directory), so a crawler can prove the page it read is the page this server sent; the check is shown below.
| status | 200 |
|---|---|
| content-type | text/markdown; charset=utf-8 |
| vary | Accept |
| link | <https://crawlcheck.io/llms.txt>; rel="describedby"; type="text/plain", <https://crawlcheck.io/.well-known/api-catalog>; rel="service-desc"; type="application/linkset+json", <https://crawlcheck.io/blog/eighteen-dmarc-reports-every-failure-was-spoofing>; rel="cite-as", <https://crawlcheck.io/.well-known/http-message-signatures-directory>; rel="http-message-signatures-directory" |
| cache-control | public, max-age=900, stale-while-revalidate=2400 |
| bytes | 13,568 |
| words | 1,967 |
| sha-256 | 92621bbdb118c9b70344dd6a0f12228282b84cc2f407a226196834c834f326d1 |
| repr-digest | sha-256=:kmIbvbEYybcDRN1qDxIigoK4TML0B6ImGWg0yDTzJtE=: ← matches the body shown |
| signature-input | sig1=("@authority";req "@path";req "repr-digest" "content-type");created=1794258557;expires=1794262157;keyid="1hHmE3fo1rg5Ig70wuFtJ5wBc7G-sUTgUJkxyPWFRps";alg="ed25519";nonce="5_vLiDKITXh4xFC9UzEdTw";tag="crawlcheck-page" |
| signature | sig1=:teelYebtJJNxmi2QmD5t01EN/CpnO6LTinHpfkhqns9P6hkYF/6hwRDLSroZKqzyDFMYEoaLg/y+ctQUqqe4Bw==: |
| verified | yes — signature by key 1hHmE3fo1rg5Ig70… from https://crawlcheck.io/.well-known/http-message-signatures-directory (directory valid), valid for 3600 more s. Checked here exactly as a crawler would; redo it yourself: https://crawlcheck.io/api/tool/wba |
Findings · 2026-11-06 · By [VSNARY | Emmanuel Orta](/author) # 18 DMARC reports, 7 domains: every failure was a spoofer Before moving seven small-business domains from p=none to p=quarantine we read every aggregate report. Five addresses on hosting, business and residential networks were sending mail as two fencing brands. No legitimate message had failed. How the reading was done and what it showed. Between 22 October and 5 November 2026 the DMARC aggregate reports for seven domains we operate showed 18 reports in total. Every message that failed both SPF and DKIM was sent as one of two fencing brands from five addresses on hosting, business and residential networks in five countries, with 21 messages from one address alone. No message from a real sender for any of the seven domains failed. On that evidence all seven moved from p=none to p=quarantine on 6 November, and the reports are being watched for a week in case a sender we do not know about appears. On this page - The reports - What a spoofer looks like in a report - The decision - Doing this on your own domain [DMARC](/glossary/dmarc) at `p=none` does nothing except produce reports. It is the right first setting, because moving to quarantine or reject before you know who sends mail as your domain can silence a real sender. The reports are how you find out. This is what two weeks of them looked like for seven domains we operate, and why the reading took the policy to quarantine the same day. ## The reports # All seven domains publish the same reporting address, so one mailbox held everything: 18 aggregate reports between 22 October and 5 November 2026, each an XML file from a receiving provider listing the sending addresses it saw, how many messages each sent, and whether [SPF](/glossary/spf) and [DKIM](/glossary/dkim) passed. Reading them means parsing the XML, grouping by source address, and separating passes from failures. | Source address network | Country (by address) | Messages | SPF | DKIM | | --- | --- | --- | --- | --- | | Hosting provider | United Kingdom | 21 | fail | fail | | Hosting and VPN provider | Spain | 6 | fail | fail | | Business network provider | South Africa | 4 | fail | fail | | Local internet provider | Indonesia | not kept | fail | fail | | Residential broadband | Slovakia | not kept | fail | fail | | Any of our real senders | Any | 0 failed | pass | pass | Every failing message was sent as one of the two fencing brands; the reports do not tie each address to one brand, so the table does not either. Countries are where each address is registered, read from its network record, not where the sender is. The message counts for the last two addresses were not kept from the reading; the first three were. The last row is the one that decides the policy: in two weeks, no message from a real sender for any of the seven domains failed. The failure set was entirely outside senders. ## What a spoofer looks like in a report # Every failing row shared two features. The source address belonged to a hosting, business or home broadband network, never to a mail provider. And the forged From domain was always one of the two fencing brands. The reports do not say why those two; the likeliest reason is that a name a recipient might recognise is worth imitating, but that is an inference, not something the reports show. None of this mail was addressed to us, so we never saw its content. The reports only say that a receiver got a message claiming to be from our domain and that it failed. With `p=none`, the receiver was free to deliver it anyway. ## The decision # The question before changing the policy is whether any real sender would be caught. The reports answered it: zero legitimate failures in two weeks. All seven domains moved to `p=quarantine` on 6 November, through the DNS provider's API so that the seven records changed together, and the new records were read back from a public resolver before the change was called done. Quarantine rather than reject for now, because quarantine sends a failing message to spam instead of refusing it, so a sender we have not identified would still arrive somewhere a person can see. The reports continue to come in. If a passing-SPF or passing-DKIM failure appears for a real sender in the next week, that sender gets added to the record before anything else changes. If a week passes clean, reject is the next step. ## Doing this on your own domain # The procedure is in [the DMARC guide](/guides/how-to-move-dmarc-from-none-to-quarantine-without-losing-mail). The part people skip is the one that mattered here: read the reports before you change anything, and read them as a table grouped by source address. A list of 18 XML files tells you nothing. A six-row table tells you exactly who is sending as you, and whether any of them is you. Two cautions from this reading. First, the reporting address must be a mailbox someone can actually open; ours was a separate business mailbox and the reading waited until it was. Second, a domain that sends almost no mail still gets spoofed. Low volume is not protection; only an enforced policy is. Written by [Emmanuel Orta](/author) · VSNARY I build CrawlCheck and write everything published here, from measurements the verification layer took. Where a post got something wrong, the correction stays in the post. [Every post, and where I got it wrong →](/author) · [Follow on LinkedIn →](https://www.linkedin.com/in/emmanuelorta) Share this finding: [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcrawlcheck.io%2Fblog%2Feighteen-dmarc-reports-every-failure-was-spoofing%3Futm_source%3Dlinkedin%26utm_medium%3Dsocial%26utm_campaign%3Dshare)[X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fcrawlcheck.io%2Fblog%2Feighteen-dmarc-reports-every-failure-was-spoofing%3Futm_source%3Dx%26utm_medium%3Dsocial%26utm_campaign%3Dshare&text=18%20DMARC%20reports%2C%207%20domains%3A%20every%20failure%20was%20a%20spoofer)[Email](mailto:?subject=18%20DMARC%20reports%2C%207%20domains%3A%20every%20failure%20was%20a%20spoofer&body=https%3A%2F%2Fcrawlcheck.io%2Fblog%2Feighteen-dmarc-reports-every-failure-was-spoofing)[Follow Emmanuel on LinkedIn](https://www.linkedin.com/in/emmanuelorta) ## Questions this post answers ### What did the DMARC reports show? 18 aggregate reports over two weeks for seven domains. Every failing message came from one of five outside addresses on hosting, business or residential networks in five countries, forging two fencing brands. No legitimate message failed. ### Why move to p=quarantine instead of p=reject? Quarantine sends a failing message to spam rather than refusing it, so a real sender nobody had identified would still land somewhere visible. After a week of clean reports, reject is the next step. ### How do you read a DMARC aggregate report? Parse the XML, group rows by source address, and separate messages that passed SPF or DKIM from those that failed both. A table of a few rows by source address shows who is sending as your domain. ### Why were only two of the seven domains forged? The reports do not say. The likeliest reason is that a spoofer picks a name a recipient might recognise, but that is an inference rather than something the reports show. ### Does a low-volume domain need DMARC enforcement? Yes. The forged domains here send very little mail themselves. Enforcement protects recipients from mail that claims to be you, which has nothing to do with how much you send. ### What was checked before changing the policy? That zero legitimate messages had failed in two weeks of reports, that the reporting mailbox was readable, and after the change, that all seven new records resolved from a public DNS resolver. ## Related findings [2026-09-30 Five days of our alerts went to an address with no mailbox After a mail move, our alert address had no mailbox. For five days every alert said sent and none arrived. Why checks passed, and](/blog/five-days-of-our-alerts-went-to-an-address-with-no-mailbox)[2026-09-30 How to move DMARC from p=none to p=quarantine without losing email Moving DMARC from none to quarantine is safe when every sender aligns DKIM and the report address exists. The order of those steps](/guides/how-to-move-dmarc-from-none-to-quarantine-without-losing-mail)[2026-09-01 Dead link or blind spot? When a checker's failure is its own Our sameAs check called a live profile page dead because our own fetch failed. It now reports such targets as unverifiable. How to](/blog/the-scanner-that-cannot-fetch-its-own-host) The main product Found this on your own site? **We fix it for $100.** Scan free to see where you stand. The fix is one site, every finding implemented and re-measured, with a sealed before and after. [Fix it all — $100](https://buy.stripe.com/4gM00j7OjawhdLEdyR2VG0v) [What it covers](/pricing#fix) · [scan first, it is free](/) Free browser extension Check this on any site you open, **without leaving the page.** CrawlCheck for Chrome grades every site you visit A–F and shows, from your own browser, what search engines and AI assistants are actually served. Free, no account. [Add to Chrome — free](https://chromewebstore.google.com/detail/plgjengobhbdmbgckiiaaflgiepdbcpi?utm_source=crawlcheck.io&utm_medium=post) [What it does](/extension) Write for the library Measured something yourself? **Submit an article.** Send your finding, the data and the method. A person reads every submission and replies by email; accepted articles are published under your name with a link to your study. Free to submit, and placement is never for sale. [Submit an article](/submit-finding#send) [How it works](/submit-finding) · a reply within about a week Spotted a mistake or a bug on this page? [Report an error](/report-error?k=article). Confirmed errors are fixed and logged on the [corrections page](/corrections). ## Comments Comments are read before they appear. Nothing is published automatically, and no account is needed. An AI agent with a published signing key can post without the queue: it signs the request and the signature is checked against its own domain ([how](/api/comment/signed)). Those appear at once, marked *verified*. Send for review Writing about this? [Facts, live figures and marks](/press) — every number on that page is dated and traceable to a scan. [All findings](/blog) · [The dataset](/data) · [How the dataset works](/policy) We read your site the way crawlers do, from our own network, and every report says exactly what was asked for and what came back. [Who runs this](/author) · [Press kit](/press) · [Videos](/videos) · [Evidence for disputes](/evidence) · [YouTube](https://www.youtube.com/@crawlcheck) · [hello@crawlcheck.io](mailto:hello@crawlcheck.io) #### How we measure, and our own machine files CrawlCheck reads every site as itself. Its crawler-identity tests send named user-agent strings from its own network, never from an operator’s published range, so a site that verifies crawlers can tell them apart; every report says so. Results reflect what an anonymous non-browser client receives. [What we collect and how to opt out](/policy) · [how every report is timestamped](/proof) · [who runs this](/author) · [press kit](/press) · [hello@crawlcheck.io](mailto:hello@crawlcheck.io). **Second vantage:** the [browser extension](/extension) measures what crawlers are served from your own address and after your JavaScript runs — the half this server cannot see. From 1.0.0 it sends the domain you ask about, on one button, and anonymous usage counts you can switch off; from 1.0.3 a footer switch turns off the hostname lookup for the grade on the icon; from 1.0.4 a share switch, on by default, sends a signed Two-vantages reading (status, size, words and a hash per identity; never page text); from 1.0.16 chips send the hostnames of search results and AI citations while the chips and grade switches are on; from 1.0.17 a readings switch, on by default, sends one signed page reading a day (numbers, the hostname and the page address with the query removed; never in incognito; at most 40 a day) and only while the share and grade switches are also on. Earlier versions send nothing. **Our own machine layer for the agentic web:** [llms.txt](/llms.txt) · [llms-full.txt](/llms-full.txt) · [agents.md](/agents.md) · [SKILL.md](/SKILL.md) · [entitymap.json](/entitymap.json) · [media kit](/.well-known/media-kit.json) · [MCP server card](/.well-known/mcp/server-card.json) · [API catalog](/.well-known/api-catalog) · [OpenAPI](/openapi.json) · [robots.txt](/robots.txt) · [sitemap.xml](/sitemap.xml) · [security.txt](/.well-known/security.txt) — every one named in robots.txt, which is exactly what we check for on yours. **Registry:** [MCP](/registry/mcp) · [app.alkhemyst-ai.cloud](/registry/app.alkhemyst-ai.cloud) · [comillas.edu](/registry/comillas.edu) · [ecomdly.com](/registry/ecomdly.com) · [huawei.com](/registry/huawei.com) · [machined.ai](/registry/machined.ai) | **Pages:** [Listing check](/listing-check) · [Entity map validator](/entity-map-validator) · [Status](/status) | **Glossary:** [Content-Security-Policy](/glossary/content-security-policy) · [protected-resource metadata](/glossary/protected-resource-metadata) | **Directory:** [Bloomreach](/directory/bloomreach) [Contact](/contact) · [Partnerships](/partnerships) · [Terms of service](/terms) · [Refund policy](/refunds) · [Trust center](/trust) · [Report an error](/report-error)
Same body as a file: /blog/eighteen-dmarc-reports-every-failure-was-spoofing.md · as data: /feed.json · what we tell agents site-wide: /llms.txt, /agents.md. To see any other site this way, from your own browser, after its JavaScript runs: the browser extension.