Read first plan legend
Why plan the exits now
The RFC behind security.txt says that having no file at all may be better than having a stale one, and that line explains the whole job. Someone who finds a flaw in your tool will look for a way to tell you privately. If the address bounces or the page is gone, many of them will post it in a public issue, or not report it at all.
A small tool needs three doors, each in the place a different kind of reporter looks: a file on your website, a file in your repository, and a private form on GitHub. Each one below has the exact text to copy, and the fire drill at the end tests that all three still open. Everything here was checked against RFC 9116 and GitHub's docs on 4 November 2026.
Exit 1 /.well-known/security.txt
A text file on your domain
RFC 9116 defines a plain text file that tells researchers how to reach you. Put it at https://example.com/.well-known/security.txt. The path must be under /.well-known/, the file must be served over HTTPS, and its Content-Type must be text/plain with a UTF-8 charset. A copy at the top level /security.txt is allowed for older tools, but when both exist the one under /.well-known/ wins.
# /.well-known/security.txt
Contact: mailto:[email protected]
Contact: https://github.com/example/tool/security/advisories/new
Expires: 2027-10-31T23:00:00.000Z
Policy: https://example.com/security
Preferred-Languages: en
Canonical: https://example.com/.well-known/security.txt
Only two lines are required. Contact must be present, and if you list several, the first is the one you prefer. Expires must appear exactly once, and the RFC recommends a date less than a year ahead. Every other field is optional.
Every field, and its rule
| Field | Needed | Rule from RFC 9116 |
|---|---|---|
Contact | Required | A URI: mailto:, tel: or an https:// page. List in order of preference. |
Expires | Required, once | An RFC 3339 date and time. Less than a year ahead is recommended. |
Encryption | Optional | A link to your key, never the key itself. |
Policy | Optional | A link to your disclosure policy: scope, what you promise, what testing you allow. |
Acknowledgments | Optional | A link to a page thanking reporters. |
Preferred-Languages | Optional, once | Language tags such as en, de. Without it, English is assumed. |
Canonical | Optional | The URLs where this file lives. Recommended when you sign the file. |
Hiring | Optional | A link to security job openings. |
One file covers one host. Section 3.1 says it applies only to the domain it was fetched from, not to subdomains, so docs.example.com and app.example.com each need their own copy or a redirect.
Exit 2 SECURITY.md
A policy file in your repository
Most people who find a bug in a library never visit your website. They open the repository. GitHub calls this file a security policy: a SECURITY.md that says which versions you still support and how to report a vulnerability. You can start one from the repository's "Security and quality" tab, under "Security policy".
GitHub reads the file from three places, in this order: the .github folder, the repository root, then docs. If a repository has none, GitHub falls back to the file in your account's public .github repository, so one file can cover every project you own.
# Security policy
## Supported versions
| Version | Supported |
| ------- | --------- |
| 2.x | yes |
| 1.x | security fixes until 2027-03-31 |
| < 1.0 | no |
## Reporting a vulnerability
Please do not open a public issue.
Use "Report a vulnerability" on this repository's
Advisories page, or email [email protected].
We reply within 5 working days.
The reply time in that sample is a promise, so only write one you will keep. For a longer real example, GitHub's own docs point to the SECURITY.md of the Electron project.
Exit 3 Advisories page
A private report form on GitHub
Email works, but it leaves the reporter guessing what to include. Owners and admins of public repositories can turn on private vulnerability reporting under Settings, then Advanced Security, next to "Private vulnerability reporting". Researchers then see a "Report a vulnerability" button on the repository's Advisories page, and the report stays private between them and the maintainers.
By default the form asks for a summary, details, a proof of concept and an impact statement. GitHub's docs say the form can be changed with the same YAML syntax as issue forms, with fields you can mark as required. That structure saves the first round of "can you send steps to reproduce?" emails.
Point the other two doors here. The second Contact line in the security.txt above and the reporting section in the SECURITY.md both send people to /security/advisories/new, so the three exits meet in one room you already check.
Exit 4 fire drill
Fire drill: is this file valid?
Five short cases. Decide, then open each one to check against the RFC.
The file has a Contact line and a Policy line, and no Expires line. Valid?
No. Expires is required and must appear exactly once. Without it, a researcher cannot tell whether the contact is still current.
Contact: [email protected]. Valid?
No. Contact must be a URI, so an email address is written mailto:[email protected]. A phone number is written with tel:.
The file is served at http://example.com/.well-known/security.txt and nowhere else. Valid?
No. The file must be fetched over HTTPS. Serve it on https and redirect plain http to it.
Your file sits on example.com. Does it also cover api.example.com?
No. A security.txt applies only to the host it was fetched from, not to subdomains or parent domains.
Does publishing a security.txt mean anyone may scan or attack your site to find bugs?
No. The RFC says the presence or absence of the file grants or denies no permission to test. If you allow testing, say so, with limits, in the page your Policy field links to.
Exit 5 every 6 months
The drill to repeat
Doors that nobody tests stop opening. Put this list in your calendar twice a year, and also on the day a maintainer leaves.
- Fetch
/.well-known/security.txtover HTTPS and check it returns 200 andtext/plain. - Move
Expiresforward so it stays under a year ahead. A file past its date is stale by its own rules. - Send a test mail to every
mailto:Contact and confirm a person who is still on the team reads it. - Open the "Report a vulnerability" button on each public repository and check it is still enabled.
- Read the supported versions table in
SECURITY.mdagainst your actual release lines. - If the file is signed, sign it again after every edit, and keep the
CanonicalURLs current.
Exit 6 sources
Sources
- RFC 9116, A File Format to Aid in Security Vulnerability Disclosure, IETF, April 2022
- GitHub Docs, Adding a security policy to your repository
- GitHub Docs, Creating a default community health file
- GitHub Docs, Configuring private vulnerability reporting for a repository
All four were read on 4 November 2026.



