# security.txt

> security.txt tells researchers where to report a vulnerability. RFC 9116 requires a Contact and an Expires date, and an expired file scores 0.

security.txt is a small text file at `/.well-known/security.txt` that tells security researchers how to report a vulnerability. It is defined by [RFC 9116](https://www.rfc-editor.org/rfc/rfc9116), which requires a contact and an expiry date. It carries 12 of the 100 points in Flowpane's site score, and an expired file scores 0.

## What it is for

The file is read by independent researchers, coordinated disclosure teams and automated scanners looking for the right channel before they send a report. It answers practical questions: where to send the report, in which language, how to encrypt it and under what disclosure policy.

Without the file, reports go to general inboxes, social media or nowhere. With a stale one, they go to a mailbox nobody reads, which is worse, because the sender believes the report was delivered.

## Why Expires exists

The RFC assumes contact details decay. People leave, aliases are retired and reporting platforms change. `Expires` forces a review date into the file itself. Once it passes, RFC 9116 says the file should be considered stale and not used.

This is why Flowpane treats expiry as a failure rather than a warning. An expired security.txt still looks authoritative to anyone who does not check the date, and it points them at a channel that nobody has confirmed since.

## How it goes stale

- **Set once, forgotten.** `Expires` is set a year ahead at launch, then the file becomes invalid on a known date with nobody assigned to renew it.
- **Set too far ahead.** A date decades out avoids the renewal work and defeats the purpose. The RFC recommends less than a year.
- **Personal contacts.** `Contact` names an individual's address rather than a monitored team channel, and the individual moves on.
- **Wrong location or format.** The file exists only at the legacy `/security.txt`, or a catch-all route serves an HTML page at the canonical path.
- **Broken signatures.** A clear-signed file is edited by hand after signing.
- **Misspelt fields.** The registered field name is the US spelling `Acknowledgments`. The British spelling is not recognised as that field.

## What true looks like

```text
Contact: mailto:security@example.com
Contact: https://www.example.com/security/report
Expires: 2027-06-30T23:00:00Z
Encryption: https://www.example.com/.well-known/pgp-key.txt
Preferred-Languages: en, fr
Canonical: https://www.example.com/.well-known/security.txt
Policy: https://www.example.com/security/disclosure-policy
```

- At least one `Contact`, written as a URI: `mailto:` for email, `https://` for web forms. A bare email address is not valid.
- Exactly one `Expires`, as an RFC 3339 date-time with a timezone, less than a year ahead.
- Served as plain text at `/.well-known/security.txt`, with `Canonical` matching where it is actually served.
- A named owner renews the file on a calendar, well before the expiry date.

## How Flowpane checks it

Flowpane fetches the canonical location, `/.well-known/security.txt`, and records whether the request was redirected, including to the legacy `/security.txt` path or to another origin.

**Required fields.** `Contact` must appear at least once and each value must be a valid URI. Web contacts must use `https://`. `Expires` must appear exactly once, as a valid RFC 3339 date-time.

**Optional fields.** `Encryption`, `Acknowledgments`, `Preferred-Languages`, `Canonical`, `Policy` and `Hiring` are parsed and their URIs checked. Language tags are validated. Flowpane does not test whether a contact address or URL is reachable.

**Expiry rules.**

| Condition | Result |
| --- | --- |
| `Expires` in the past | Score 0, regardless of anything else in the file |
| `Expires` within 30 days | Flagged for renewal |
| `Expires` more than a year ahead | Flagged, as the RFC recommends less than a year |
| Missing or invalid `Expires` | Issue |

The expired rule is hard: a file with perfect contacts and every optional field still scores 0 once its date has passed.

**Signatures.** Flowpane detects clear-signed files and parses the signed content. It does not verify the OpenPGP signature or establish who signed it. If you edit a signed file, sign it again before publishing, and verify the signature with your own tooling.

**Missing files.** security.txt is always required, so a missing file counts as 0 in the site score. Flowpane can draft a starter file, but the starter never invents a contact or a policy URL. You supply those, and the draft must pass the same validation as anything else. A draft never changes the score. Only the next public check does.

The [cross-file coherence](/learn/cross-file-coherence) engine also compares security.txt with other files. If ai.txt and security.txt name different contacts, that disagreement is reported.

## Common failures

| Symptom | What it means | Effect |
| --- | --- | --- |
| `Expires` date has passed | File is stale under RFC 9116 | Score 0 |
| No `Contact` field | No reporting channel | Issue |
| Contact is a bare email address | Not a valid URI | Issue |
| Empty body or HTML at the canonical path | Catch-all route, no real file | Score 0 |
| 404 at `/.well-known/security.txt` | Required file missing | Counts as 0 |

## Related

- [Disclosure](/use-cases/disclosure)
- [ai.txt](/learn/ai-txt)
- [Cross-file coherence](/learn/cross-file-coherence)
- [Stale files](/use-cases/stale-files)
- [Scoring](/learn/scoring)
