Website problems
No security.txt file
`security.txt` is a plain-text file at `/.well-known/security.txt` that tells anyone who finds a security problem how to report it. It is specified in RFC 9116, and its absence means a researcher with a genuine finding has to guess — a support form, a generic address, a social media account — and many will give up or disclose publicly instead. It costs nothing to publish and it is the difference between learning about a problem privately and reading about it.
What the file contains
`Contact:` is the only required field, and it may repeat. A `mailto:` address, a URL to a reporting form, or a telephone number, in order of preference.
`Expires:` is also required. It is a timestamp after which the file should no longer be considered current, which forces the contents to be reviewed rather than left to rot.
`Encryption:` points at a public key so a report can be sent encrypted.
`Policy:` links to your disclosure policy — what is in scope, what you ask of reporters, what they can expect.
`Acknowledgments:` links to a page thanking people who have reported, which costs nothing and materially increases the number of reports you get.
`Preferred-Languages:` lists the languages your team reads, which prevents a report being written in one nobody can act on.
The Expires field, and why it is required
It was made mandatory because the predictable failure of this file is abandonment: a contact address for someone who left, a policy link that 404s, a file nobody has read in four years.
An expired file is worse than none, because a reporter follows it, receives no reply, and concludes the organisation does not care — having done exactly what was asked.
The specification recommends a value less than a year in the future, which turns the file into a recurring review rather than a one-off publication.
So the maintenance obligation is the point, not an overhead. A file with a fresh `Expires` is a statement that someone checked recently.
Where it goes and how it is served
`/.well-known/security.txt` is the canonical location. A copy at the root is permitted for legacy reasons and the well-known path is the one that should exist.
It must be served over HTTPS, as `text/plain`, with a `charset` of `utf-8`.
A common mistake is serving it as `text/html` because the web server guesses the type from a missing extension, which makes it non-conforming.
The file may be signed with an OpenPGP signature, which lets a reporter verify it was published by you rather than by someone who compromised the site.
For a site with several domains, each one needs its own file, since a reporter looking at one domain will not think to check another.
What it must not contain
Anything that is not a contact route. The file is public, indexed, and fetched by every scanner on the internet.
Internal hostnames, ticketing URLs behind a VPN, staging addresses, or the names of internal systems.
Personal contact details of individuals rather than a role address. People change roles; the address should not.
Anything resembling a credential, including links containing tokens.
And it must not promise more than the organisation will deliver. A policy stating a 24-hour response that nobody honours produces exactly the disappointment the file was meant to avoid.
How to check it yourself
`curl -I https://example.com/.well-known/security.txt` — a 200 with `Content-Type: text/plain; charset=utf-8` is correct.
`curl -s https://example.com/.well-known/security.txt` and read it. Confirm `Contact` and `Expires` are present and that `Expires` is in the future.
Follow every URL in the file. A `Policy` link that 404s is the most common defect in a file that otherwise exists.
Send a test report to the contact address and see whether it reaches someone. A valid file pointing at an unmonitored inbox passes every automated check and fails the only test that matters.
How VeriFixScan detects it
`exposure.security_txt.present` requests the file at the well-known location and reports whether it exists and is served correctly.
`exposure.security_txt.fields` reports the fields it contains, `exposure.security_txt.expired` reports whether the `Expires` timestamp has passed, and `exposure.security_txt.policy` reports whether a policy is linked.
`exposure.security_txt.location` reports where the file was found, since a file only at the root rather than under `/.well-known/` is a partial implementation.
The checks read a public file. Whether the contact address is monitored is not observable from outside, and the report says so.
Frequently asked questions
- Where exactly should security.txt live?
- At /.well-known/security.txt, served over HTTPS as text/plain with a utf-8 charset. A copy at the web root is allowed for legacy reasons but is not the canonical location.
- Why is the Expires field mandatory?
- Because the predictable failure of this file is abandonment. An expiry date forces periodic review, and RFC 9116 recommends a value less than a year ahead.
- Do I need a bug bounty to publish one?
- No. The file states how to report a problem. A policy explaining that you accept reports and do not pay for them is a complete and honest policy.
Sources
Related
VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.
Scan a website