How to set up security.txt, step by step
A security.txt tells anyone who has found a security flaw in your website or products where to report it. Writing the file is quick. This guide shows what belongs in it, how to sign it, how to serve it correctly and how to keep it current.
What is security.txt?
security.txt is a small text file located at https://your-domain/.well-known/security.txt. It is defined in RFC 9116, published by the IETF in April 2022. Every line is a field in the form Field-Name: value; comments start with #. Security researchers, scanners and authorities read the file automatically.
The standard is explicit about two things: the file must be served over HTTPS as text/plain with charset=utf-8 (RFC 9116 section 3), and it applies only to the exact domain it was retrieved from, not to subdomains or parent domains (section 3.1). If your site answers on both company.com and www.company.com, you need the file at both addresses.
The fields at a glance
RFC 9116 requires only two fields. Technical Guideline TR-03183-3 of the German Federal Office for Information Security (BSI) makes more of them mandatory. Newer fields such as CSAF and Bug-Bounty are listed in the IANA registry.
| Field | RFC 9116 | BSI TR-03183-3 | Content |
|---|---|---|---|
| Contact | Required, may repeat, in order of preference | Required: PSIRT mailbox first, CSIRT mailbox second, reporting page third (4.2.3) | mailto:, https:// or tel: |
| Expires | Required, exactly once, recommended less than a year ahead | Required, at most one year ahead, upper-case T and Z (4.2.9) | Date as per RFC 3339 |
| Canonical | Optional, recommended when signed | Required, reachable without redirect (4.2.2) | Address of the file itself |
| Encryption | Optional | Required for every contact address, direct .asc download (4.2.4, 4.3.2) | Address of the OpenPGP key |
| Preferred-Languages | Optional, once | Required, at least en (4.2.6) | Language tags, comma-separated |
| Policy | Optional | Required (4.2.7) | Address of the CVD policy |
| Acknowledgments | Optional | Recommended (4.2.5) | Hall of fame page |
| CSAF | Not in the RFC, IANA registry since 2023 | Recommended (4.2.8) | Address of provider-metadata.json |
| Bug-Bounty | Not in the RFC, IANA registry since March 2026 | Not covered | True or False |
| Hiring | Optional | Not covered | Security job openings |
On top of that comes the signature: RFC 9116 recommends an OpenPGP signature (section 2.3), the TR requires it (4.2.10). Complete files for both levels are on the examples page.
RFC 9116 or BSI TR-03183-3?
RFC 9116 is the worldwide standard and is enough for most websites. TR-03183-3 "Vulnerability Reports and Notifications" (version 1.0.0 of 20 August 2025) builds on it and describes how manufacturers should receive vulnerability reports: two separate role mailboxes, keys, a reporting page with a web form, a disclosure policy and a signature.
The TR is voluntary. For manufacturers of products with digital elements it is still the obvious benchmark: the EU Cyber Resilience Act (Regulation (EU) 2024/2847) requires a contact address for vulnerability reports (Annex I, Part II, point 6) but prescribes no format, and the TR is the only description of one published by a national authority. A simple company website without its own products is well served by RFC 9116.
Step 1: Create the file
The fastest way is the generator: enter your domain, choose the standard, add any missing addresses. It writes the fields in the order and with the comments the TR asks for, sets a valid expiry date and lets you copy the file only once it is free of errors. You can also write it by hand; a file under RFC 9116 can be this short:
Contact: mailto:security@example.com
Expires: 2027-10-01T00:00:00.000Z
Preferred-Languages: en
Canonical: https://www.example.com/.well-known/security.txt
Keep values in ASCII outside comments (TR 4.2.1 c). Internationalised domains go into the file as Punycode, for example xn--mller-kva.de instead of müller.de.
Step 2: Keys and signature
The signature proves that the file comes from you and not from someone trying to divert reports. You need GnuPG. The TR recommends a dedicated signing key (4.2.10 b) valid for at most five years (4.2.10 e). Create one, here with RSA 3072 bits and a two-year lifetime:
gpg --quick-generate-key "Example Ltd security.txt <security@example.com>" rsa3072 sign 2y
gpg --fingerprint security@example.com
gpg --armor --export security@example.com > security-txt-signing.asc
Sign with a cleartext signature. The file stays readable and the signature wraps around it:
gpg --clearsign --digest-algo SHA512 --local-user security@example.com --output security.txt.signed security.txt
mv security.txt.signed security.txt
Publish the public key security-txt-signing.asc on your website and list its address and fingerprint on your reporting page (TR 4.5.3 c and d). Under the TR, each role mailbox also needs its own encryption key (4.3.1 j), whose .asc file goes into the Encryption field:
gpg --quick-generate-key "Example Ltd PSIRT <psirt@example.com>" rsa3072 sign 2y
gpg --quick-add-key <fingerprint> rsa3072 encr 2y
gpg --armor --export psirt@example.com > psirt.asc
The second command adds the encryption subkey; the first command prints the fingerprint. Repeat for csirt@.
Sign again after every change. A changed line ending or a trailing space is enough to break the signature, so upload the file in binary mode, not as text.
Step 3: Serve it from your web server
The file belongs in the .well-known folder at the root of your website. The folder name starts with a dot; some FTP clients hide such folders and some server configurations block them. After uploading, check that the request returns status 200 and the right content type.
Apache
In the .htaccess at the web root or in the server configuration:
<Files "security.txt">
ForceType "text/plain; charset=utf-8"
</Files>
<FilesMatch "\.asc$">
ForceType application/pgp-keys
</FilesMatch>
nginx
Many templates deny every path that starts with a dot. Allow .well-known explicitly and set the charset:
location ^~ /.well-known/ {
allow all;
}
location = /.well-known/security.txt {
charset utf-8;
}
WordPress and other CMS
Put the file into /.well-known/ next to the WordPress folders via FTP or a file manager. The web server serves a real file before WordPress sees the request. Then check that you do not get a CMS error page with status 200 instead; that is one of the most frequent findings of the checker.
Cloudflare and other bot protection
The TR requires that scanners can retrieve the file automatically (4.2.11). Bot protection that answers automated requests with a challenge page prevents exactly that. On Cloudflare the basic Bot Fight Mode cannot be switched off for individual paths; exceptions for /.well-known/ require Super Bot Fight Mode on the Pro plan or above. The checker shows whether your protection gets in the way.
Old location /security.txt
If your file currently lives only at /security.txt, move it. The old address may redirect to the new one (RFC 9116 section 3). If the file exists in both places, the one under /.well-known/ wins.
Step 4: Check it
The checker fetches the file and verifies structure, expiry date, Canonical, links, mail domains, keys and the signature, under RFC 9116 or under the TR. By hand:
curl -sI https://www.example.com/.well-known/security.txt
curl -s https://www.example.com/.well-known/security.txt | gpg --verify
The first command must show HTTP/2 200 (or 1.1) and content-type: text/plain; charset=utf-8. The second reports "Good signature" if the public key is in your keyring. Also send a test e-mail to every address in the file and make sure it arrives and gets read.
Step 5: Keep it current
security.txt has an expiry date so that outdated information does not live forever. After it, scanners treat the file as invalid. Put three dates in your calendar:
- Before the expiry date: review the content, set a new date at most one year ahead, sign again, upload.
- Quarterly: the TR requires checking and correcting the information at least every quarter (4.2.9 c).
- Before your keys expire: extend them with
gpg --quick-set-expire <fingerprint> 2yand for the subkeysgpg --quick-set-expire <fingerprint> 2y '*', then export the .asc files again. The fingerprint stays the same.
If you would rather not keep track: our monitoring checks the file every day and reminds you in time, for EUR 60 per year per domain.
Common mistakes
An analysis of the top one million domains by uriports (24 January 2025) shows how rarely the file is right: only 1.25 percent had a security.txt, and 44 percent of those complied with the RFC. 45 percent lacked Expires, 13 percent had expired.
- Wrong location: only at
/security.txtinstead of/.well-known/security.txt. - Web page instead of file: the CMS intercepts the path and returns an error page with status 200.
- Expired Expires or a date more than a year ahead; lower-case t or z in the date.
- Canonical points elsewhere, for example to
company.comwhile the file lives atwww.company.com. - Acknowledgements instead of Acknowledgments: the field uses the American spelling.
- E-mail without mailto: in the Contact field.
- Encryption points to a web page instead of directly to the .asc file.
- Broken signature, because the file was edited after signing or rewritten during upload.
- Nobody reads the mailbox. The most consequential mistake, and no scanner can find it.
Frequently asked questions
Is security.txt mandatory?
No law prescribes the file. The Cyber Resilience Act requires manufacturers to provide a contact address for vulnerability reports, but no format. BSI TR-03183-3 requires security.txt but is voluntary. For everyone else it is a recommendation, including by national cybersecurity agencies.
Which fields are required?
Under RFC 9116 only Contact and Expires. Under the TR also Canonical, Encryption, Preferred-Languages with at least English, Policy and the signature.
How far ahead may the expiry date be?
RFC 9116 recommends less than a year, the TR at most one year. The generator suggests one year minus one day.
Is security@ enough as an address?
For RFC 9116, yes. The TR requires two role mailboxes, psirt@ for products and csirt@ for your own IT. Both may reach the same team.
Do I need the file on every subdomain?
The file applies only to the domain it is served from (RFC 9116 section 3.1). A separate file makes sense for every domain and subdomain with its own website, at least for the main domain with and without www.
Do I have to sign the file?
RFC 9116 recommends it, the TR requires it. Without a signature nobody can be sure the contact addresses really come from you.
Where do researchers report if there is no security.txt?
Often to a general address nobody associates with security, or not at all. Some then publish the flaw directly. That is exactly what the file is meant to prevent.
Sources
- RFC 9116: A File Format to Aid in Security Vulnerability Disclosure, IETF, April 2022.
- BSI TR-03183-3 Vulnerability Reports and Notifications, version 1.0.0, Federal Office for Information Security, 20 August 2025.
- IANA registry: security.txt Fields, as of 7 March 2026.
- Regulation (EU) 2024/2847 (Cyber Resilience Act), Annex I, Part II, point 6.
- Cloudflare Docs: Bot Fight Mode, retrieved 10 October 2026.
- security.txt in 2025, uriports, 24 January 2025.