security.txt: how to publish a security contact

What a security.txt file is, where it goes, which lines it needs, and how to keep its Expires date current.

When someone finds a security problem on your site, they need to know who to tell. Without a published contact, they have to guess, and a report can end up nowhere. security.txt, defined in RFC 9116, puts that contact in one standard place that people and scanners know to look.

It’s optional, and RFC 9116 says having no file may be better than one with stale details, which send reports to the wrong place.

What SignalDNS checks

We request /.well-known/security.txt on the host of the page we audited, following up to three redirects, and read up to 32 KiB of it. It only counts if it answers with a 200 and plain text. Some sites answer every address with an HTML page, and that isn’t a security.txt.

Since the file is optional, the question only appears when there is one; otherwise Details shows “None”. When there is a file, we check its Contact and Expires lines and its format. Field names are read in any letter case, lines starting with # are comments, and a signed file is read between its signature lines.

RFC 9116 says a file applies only to the host it’s served from, not to subdomains or parent domains. example.com and www.example.com each need one. A redirect from one to the other is allowed, and we follow it.

What a security.txt file looks like

Serve it at https://example.com/.well-known/security.txt, as text/plain in UTF-8:

Contact: mailto:[email protected]
Contact: https://example.com/security/report
Expires: 2027-06-30T00:00:00Z
Encryption: https://example.com/security/pgp-key.txt
Policy: https://example.com/security/policy
Acknowledgments: https://example.com/security/thanks
Preferred-Languages: en, es
Canonical: https://example.com/.well-known/security.txt
  • Contact is required, and can appear more than once, most preferred first. Use a mailto: or tel: address, or an https:// page.
  • Expires is required, exactly once: the date and time after which the file shouldn’t be trusted.
  • Encryption links to a key for encrypted reports. Never paste the key itself.
  • Policy links to your disclosure policy: what you accept, and how you respond.
  • Acknowledgments links to a page thanking researchers, and Hiring to security jobs.
  • Preferred-Languages lists languages for reports, once at most. Without it, researchers may assume English.
  • Canonical lists the addresses where this file is meant to live.

Every web address in the file must start with https://.

security.txt has no contact

The file has no Contact line, so it doesn’t say how to report a problem, which is the reason it exists. SignalDNS marks this as needing attention. When there is one, we show the first, since that’s the preferred one.

How to fix it: add at least one Contact line. Use an address someone reads and that won’t change when people do, such as a team mailbox:

Contact: mailto:[email protected]

The Expires date is missing, malformed, or has passed

RFC 9116 requires exactly one Expires line, so that old contact details stop being trusted. We report these states:

  • Missing. There’s no Expires line. This needs attention.
  • Not in the required format. RFC 9116 uses the RFC 3339 date-time: a date, a time, and a time zone, such as 2027-06-30T00:00:00Z. A date alone, such as 2027-06-30, a time without a zone, or a date that doesn’t exist, such as 30 February, doesn’t qualify, even if some tools would read it. This needs attention.
  • Passed. The date is before the moment we checked, so people may treat the contacts as out of date. This needs attention.
  • More than one Expires line. RFC 9116 allows only one, so tools can’t tell which date counts. This needs attention. We use the first line’s date for the other states.
  • More than a year ahead, by over 366 days. RFC 9116 recommends less than a year, to avoid stale files. This is a note, not counted.
  • Otherwise, it’s OK, and we show the date.

How to fix it: keep one Expires line, set less than a year away, in the RFC 3339 format the standard uses, with a time and a time zone:

Expires: 2027-06-30T00:00:00Z

Put a reminder in your calendar a few weeks before. Each time, check that the contacts and links still work, update the date, and sign the file again if you sign it.

security.txt has mistakes

The file has a problem that can make tools distrust or skip it. SignalDNS marks this as needing attention:

  • Canonical lines that name other addresses. None of them is the address we read the file from, after any redirects. RFC 9116 says such a file shouldn’t be trusted. It can mean the file was copied from another site, or that example.com redirects to www.example.com and only one of them is listed.

Problems with the Contact and Expires lines are reported under their own checks, Contact and Expires.

How to fix it: list every address the file is served at, exactly, each on its own Canonical line:

Canonical: https://example.com/.well-known/security.txt
Canonical: https://www.example.com/.well-known/security.txt

Signing the file

RFC 9116 recommends signing the file with an OpenPGP cleartext signature, and including Canonical when you do, so the signature also confirms where the file belongs. It’s optional. With GnuPG, gpg --clear-sign security.txt writes a signed copy to publish in its place.

We read a signed file between its signature lines and note in Details that it’s signed. We don’t verify the signature; RFC 9116 leaves it to researchers to decide whether they trust the key.

Setting it up in your CMS or framework

What we can’t tell from outside

We check one host, at /.well-known/ only, not the older /security.txt location. We don’t test whether the contacts work, check the syntax of each value, or verify signatures. A file we mark OK can still list a mailbox nobody reads.

Sources

Last reviewed .