security.txt Generator

An RFC 9116 security.txt with the Expires field it requires, which is the field most of the files on the web are missing or have let lapse.

Enable JavaScript to customise; default output below.

A mailbox somebody reads. A ticket queue nobody watches is worse than no file at all, because it promises a response.

Optional second contact. Listed after the email, because the order is the order you prefer to be contacted in.

Required by RFC 9116. A year out is the usual choice; past this date the file is formally invalid, and most security.txt files in the wild are expired.

Where this file officially lives. It stops a copy of your file on another domain being treated as yours.

What a reporter can expect: scope, response times, whether you will take legal action. The single most useful optional field.

Where you credit reporters. Note the spelling: the field is Acknowledgments, without the middle e, whatever your dictionary says.

Language tags, in your order of preference. It appears once with a comma-separated list, not once per language.

A link to the key, not the key itself. A fingerprint goes in as openpgp4fpr:….

Only if you publish machine-readable advisories. Most sites leave this empty.

In the RFC, and entirely optional.

Live preview security.txt
# security.txt, as defined by RFC 9116.
#
# Upload this to BOTH of these paths, with the .well-known one as canonical:
#   /.well-known/security.txt
#   /security.txt
#
# It has to be served over https, as text/plain, and without a redirect to
# somewhere else. Expires is required: past that date the file is invalid.

Contact: mailto:[email protected]
Contact: https://example.com/security
Expires: 2026-12-31T23:59:59.000Z
Encryption: https://example.com/pgp-key.txt
Acknowledgments: https://example.com/hall-of-fame
Preferred-Languages: en, de
Canonical: https://example.com/.well-known/security.txt
Policy: https://example.com/security-policy

Output is valid and updates as you type.

A security.txt file tells somebody who has found a vulnerability in your site where to send it. RFC 9116 defines it, and it has exactly two required fields: Contact and Expires.

Expires is the one everybody forgets. Past that date the file is formally invalid, and a large share of the security.txt files on the web are expired, which is worse than absent: it says nobody has looked at this in over a year.

How to use

  1. Put in a contact address somebody actually reads.
  2. Set Expires about a year out, and put a calendar reminder in for a month before.
  3. Fill in the optional fields you have. Policy is the most useful of them.
  4. Upload to /.well-known/security.txt, and put a copy at /security.txt.

Example

# security.txt, as defined by RFC 9116.
#
# Upload this to BOTH of these paths, with the .well-known one as canonical:
#   /.well-known/security.txt
#   /security.txt
#
# It has to be served over https, as text/plain, and without a redirect to
# somewhere else. Expires is required: past that date the file is invalid.

Contact: mailto:[email protected]
Contact: https://example.com/security
Expires: 2026-12-31T23:59:59.000Z
Encryption: https://example.com/pgp-key.txt
Acknowledgments: https://example.com/hall-of-fame
Preferred-Languages: en, de
Canonical: https://example.com/.well-known/security.txt
Policy: https://example.com/security-policy

Two Contact lines are allowed and the order is meaningful: it is the order you prefer to be contacted in.

Pitfalls

A contact nobody reads is worse than nothing. The file is a promise that a report will reach a person. If it lands in a shared inbox with four hundred unread messages, the reporter’s next step is usually a public disclosure.

Expires is required and it is a real expiry. A year is the usual choice. Renewing it is a one-line edit, and the reminder to do it is the actual work.

It has to be served as text/plain over https. A server that returns it as text/html, or a redirect to a pretty page, fails the format. WordPress will happily serve a static file from the web root; if a plugin is rewriting everything, check the response headers.

Both paths, one canonical. /.well-known/security.txt is the location the RFC defines. A copy at /security.txt is permitted for the people and scanners that look there first, and the Canonical field says which one is official.

Acknowledgments, with no middle e. The field name is fixed by the RFC and a misspelling is an unknown field. Same for Preferred-Languages, which appears once with a comma-separated list rather than once per language.

Do not put the key in the file. Encryption takes a URL to a key, a dns: URI or an openpgp4fpr: fingerprint. A PGP block pasted into the file is not what the field means.

A security.txt invites reports, including bad ones. Expect automated mails asking for a bounty for a missing header. The Policy field is where you say what is in scope and whether you pay, which cuts most of that down.

Compatibility

Everything runs in the browser: nothing is uploaded and nothing is stored.

The field order in the output is the conventional one, which does not matter to a parser: RFC 9116 requires Contact and Expires to be present, and only Canonical and Contact may appear more than once. Comments start with # and are ignored.

Expires is written as a full ISO 8601 timestamp with a Z, which is what the RFC asks for. A bare date is a common mistake and a strict parser rejects it.

For WordPress, the simplest route is a static file in the web root: wp-content is not involved, and you want /.well-known/security.txt to exist on disk. If your host blocks dot-directories, a rewrite rule to a file elsewhere works, as long as the response is a 200 with Content-Type: text/plain rather than a redirect.

Signing the file with PGP is allowed by the RFC and rarely done. If you do, the signed block replaces the plain file and Canonical becomes load-bearing.

Frequently asked questions

Do I need one?
If you have anything worth reporting, yes, and it costs ten minutes. It is also increasingly checked: bug bounty platforms, security scanners and some procurement questionnaires look for it.
What should the Policy page say?
What is in scope, what is not, how fast you respond, whether you will take legal action, and whether you pay. Saying “no bounty, and we will not sue you for a good-faith report” is a complete and useful policy.
Will it attract attackers?
There is no evidence for that, and a scanner does not need your permission. What it does attract is low-quality reports, which the Policy field exists to filter.
How do I check it is valid?
Fetch it over https and confirm the status is 200, the content type is text/plain, and there is no redirect. There are online validators for RFC 9116 as well, and the two fields they check first are the two required ones.
What if my site is on a subdomain?
Each host that serves a site should have its own file. A security.txt on example.com does not cover shop.example.com, and Canonical makes the intended scope explicit.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.