← security.txt Checker

Why RFC 9116 made security.txt expire

Most conformance rules are static. You fix the file once, and it stays fixed until somebody edits it. Expires is not like that: it is a date, so a file that passes every check today fails on its own — with no commit, no deploy, and nothing in any log to point at. That is not an oversight in the specification. It is the specification's main idea.

The problem the field exists to solve

Before RFC 9116, the worst outcome for a published disclosure contact was not a missing file. It was an abandoned one. A security.txt published in 2019, naming an address that stopped being monitored in 2021, gives a researcher every reason to believe they have reached the right place — and no way at all to tell that they have not. They send the report, they wait, they hear nothing, and eventually they conclude you do not care. From your side, nothing happened, because nothing arrived.

A mandatory expiry date makes that staleness explicit. After the date, the file is no longer vouching for itself, and a consumer knows to stop trusting it rather than having to infer abandonment from silence.

What an expired file actually does

RFC 9116 states that data in an expired file must be considered stale and should not be used. So to a conforming researcher or automated consumer, an expired security.txt publishes nothing at all. A person holding a real finding falls back to whatever else they can find — a support form that routes to billing, a public issue tracker, a social post, or nobody.

Meanwhile: the server returns 200. The text still reads correctly to a human. Every uptime check, every link checker, and every deploy pipeline you have stays green. This failure has no symptom on your side, which is exactly why it is worth checking deliberately.

Choosing a value

The RFC recommends less than a year ahead. Two competing pressures decide where inside that range you land:

The way out of that trade-off is not a cleverer date, it is automation. If a scheduled job checks the file, the expiry length stops being a bet on your own memory:

node check.mjs example.com   # exit 1 once Expires has passed

That is the one check in this catalogue genuinely worth putting on a schedule rather than in a per-commit pipeline, because the thing that changes is the calendar, not the code.

The other silent failures

Expiry is the most common, but it shares a shape with the rest of the quiet ones: they are all legal-looking text that a conforming parser simply ignores.

Acknowledgements with the middle "e"

The RFC field is Acknowledgments. The British spelling is not a recognised field, and RFC 9116 says consumers must ignore fields they do not recognise — so the hall-of-fame page you are pointing at is simply never found. No error, no warning, nothing.

A bare address in Contact

Contact is defined as a URI field. security@example.com is not a URI; mailto:security@example.com is. A consumer that resolves URIs will skip the first one, and a human reading the file will not notice the difference.

A key pasted into the file

RFC 9116 is explicit that keys must not appear in security.txt — Encryption takes a URI that points at one. An inline PGP block also breaks the line-oriented format for every field after it, so it tends to take the rest of your file down with it.

A signed file with no Canonical

A cleartext signature proves the text is yours. It does not prove where the text was meant to live, so a signed file copied to another host verifies just as well. Canonical is what closes that gap, which is why the RFC says to use it when the file is signed.

Common questions

Why did RFC 9116 make Expires mandatory?

Because the failure mode the format had before it was an abandoned file that still looked authoritative. A security.txt published in 2019 naming an address that stopped being monitored in 2021 gives a researcher every reason to believe they have reached the right place, and no way to tell they have not. A mandatory expiry date makes staleness explicit rather than invisible: after that date, a conforming consumer knows the file is no longer vouched for, instead of guessing from a timestamp nobody publishes.

What does an expired file actually do?

RFC 9116 says data in an expired file must be considered stale and should not be used. In practice that means a conforming researcher or automated consumer treats you as publishing no disclosure contact — so a person holding a real finding falls back to whatever they can find: a support form, a public issue tracker, a social post, or nothing. The server keeps returning 200, the text still reads correctly to a human, and no monitoring you have will notice.

How long should Expires be?

RFC 9116 recommends less than a year into the future. A shorter value is a stronger claim — it says someone looked at this recently — and a very long one gives away most of the benefit, since the point is to force a periodic re-check rather than to be a formality. The practical answer is to pick a period you will actually honour and automate the renewal, because a date chosen to avoid maintenance is a date that will pass unnoticed.

What else silently fails in a security.txt?

The quiet ones share a shape: they are legal-looking text a conforming parser ignores. "Acknowledgements" with the middle e is not the RFC field name, so the page it points at is never found. A bare email address in Contact is not a URI, so a consumer that resolves URIs skips it. An unknown field name must be ignored by conforming parsers, per the RFC, so anything you invented does nothing. None of these produce an error anywhere — the file parses, serves, and under-delivers.

Check your own file: security.txt Checker. See also: how this differs from securitytxt.org's generator and validator.