What does a working security.txt file example look like? A plain text file served over HTTPS at /.well-known/security.txt with two required fields, Contact and Expires, as RFC 9116 sets out. The file is the easy part. What matters is that the address in it reaches a person, and that you have sent it a test report to prove it.
What a security.txt file is
A security.txt file is a plain text file at /.well-known/security.txt that tells anyone who finds a vulnerability how to reach you. RFC 9116, published in April 2022, defines it: Contact and Expires are required, every other field is optional. The file fixes nothing in the app; it makes the first report reach you.
RFC 9116 is an IETF document published “for informational purposes”, and it says of itself that it is not an Internet Standards Track specification. Its abstract gives the reason it exists: when researchers find vulnerabilities, proper reporting channels are often lacking, and as a result vulnerabilities may be left unreported. The file lives under /.well-known/, the folder a site keeps for machine-readable files other software looks for at a fixed path. Two kinds of people read it: researchers who have found something, and a customer’s reviewer checking how your company takes outside reports. It sits alongside the rest of web app security for an AI-built app, as the part that handles what other people find.
CISA included creating a security.txt file among its priority Cybersecurity Performance Goals, and in CISA’s post on security.txt, released on December 20, 2023, it wrote: “According to a recent study, only about a half of a percent of the world’s top one million websites publish a security.txt file.”
My own reason is closer to the app. The 21 third-party apps I audited in June and July 2026 produced 958 confirmed findings, about 46 per app. Those apps were a selected set, 11 public vibe-coded apps plus a held-out group of 10 audited blind, not a random sample, so the average describes that group and is not a rate for AI-built apps in general. My reading: an app with findings in it is an app someone outside may find them in, and security.txt is used for making sure that person can tell you.
A security.txt file example: the required fields and the optional ones worth adding
A valid security.txt file needs 2 fields: at least one Contact and exactly one Expires, which RFC 9116 recommends setting less than a year ahead. Every other field is optional; Policy, Canonical and Preferred-Languages are the ones worth adding for a small team. A generator will not tell you whether anyone reads the inbox.
The example below provides a complete security.txt file for a fictional small SaaS at example.com: the two required fields, a web form as the second way to reach the team, and three optional lines.
# security.txt for example.com (replace Expires about six months out)
Contact: mailto:security@example.com
Contact: https://example.com/security/report
Expires: 2027-03-31T00:00:00Z
Policy: https://example.com/security/policy
Preferred-Languages: en
Canonical: https://example.com/.well-known/security.txt
The order of the two Contact lines matters: RFC 9116 says contacts should be listed in order of preference, the first being preferred, and that email addresses and phone numbers use the mailto and tel schemes. Expires is an RFC 3339 timestamp. My working rule is to set it about six months out, which sits inside the RFC’s under-a-year recommendation.
The table covers every field RFC 9116 defines. The first two columns follow the RFC; the last column is my advice for a small team, not the RFC’s. Any field that holds a web address must start with https://.
| Field | Required or optional | What RFC 9116 says it is for | What to put |
|---|---|---|---|
Contact | Required, must always be present; may repeat, in order of preference | A method researchers should use for reporting vulnerabilities, such as an email address, a phone number or a web page with contact information | mailto:security@ your domain first, a report form second |
Expires | Required, must always be present, exactly once | The date and time after which the file is considered stale and should not be used; recommended less than a year ahead | A date about six months out, with a calendar reminder before it |
Encryption | Optional | A URI pointing to a key researchers should use for encrypted communication; the key itself must not appear in the field | Leave it out until someone on the team can decrypt what arrives |
Acknowledgments | Optional | A link to a page where researchers are recognized for their reports | Only if you will keep that page up |
Canonical | Optional | The canonical URIs where the file is located | The file’s own URL on this host |
Hiring | Optional | A link to the vendor’s security-related job positions | Rarely worth it for a small team |
Policy | Optional | A link to where the vulnerability disclosure policy is located | The https URL of your one-page policy |
Preferred-Languages | Optional, at most once | The natural languages preferred when submitting security reports | en, or the languages your team reads |
The RFC recommends encryption when the contact is an email address, and my working rule is to publish a key only once someone on the team can decrypt what arrives and knows where the private key lives. It also recommends signing the file with an OpenPGP cleartext signature, together with a Canonical field so the signature vouches for the file’s location as well; a signature is only worth adding when someone looks after the key that made it.
If you want a security.txt file generator, start with the generator on the RFC authors’ site, a form that writes these lines. It marks Contact and Expires as required, rejects a Contact that does not start with mailto:, tel: or https://, and copies the finished file to your clipboard. It also offers a CSAF field, which a small SaaS can leave out. The file is short enough to write yourself, and the generator checks the format of the address, not whether anyone reads the mail that arrives there.
What goes wrong without it
When a researcher could not report a bug, look first at where the report ended up, not at the file format.
| What happens | Where the report goes instead | What the RFC or the reviewer says |
|---|---|---|
| No file and no obvious security address | support@, a contact form, or nowhere | The case RFC 9116 was written for: the finding may never be reported |
| A contact nobody reads: the alias points at someone who left, or a filter drops the message | An unread mailbox | Incorrect or out-of-date details can mean reports are not received, or go to incorrect contacts and expose possible security issues to third parties |
| An expired file | Researchers treat the file as stale | After the Expires date the data should not be used, and no file may be preferable to a stale one |
| A customer questionnaire asks how vulnerabilities are reported | Nowhere to point | The reviewer’s row stays answered “no” |
The questionnaire row is one of the security questionnaire examples a larger customer sends before signing, and a missing or expired file leaves that row answered no.
Picture a founder’s app with no security.txt file, where the only address on the site is a support mailbox run through a help-desk tool. Someone who found a problem in the app writes to that address, gets the help desk’s automatic reply and a ticket number, and the message waits in the queue with the billing questions, because nobody on the team treats that queue as the place security reports arrive. A researcher needs a clear way to report an issue to the right person, and this app offers none, so the problem may stay unreported. The lesson I take from it: the file is only as good as the inbox behind it, so send that inbox a test report before anyone else has to.
A disclosure contact receives what other people find. It does not replace looking yourself, which is what web application penetration testing is for.
How to publish a vulnerability disclosure contact
Publishing a disclosure contact is 7 steps: create the address, route it to two people, write a one-page policy, write the file, serve it at the fixed path over HTTPS, send a test report from outside, and set a reminder before the file expires.
The responsible disclosure setup checklist below follows the order the work depends on: the file points at the address and the policy, so both come first. The field rules come from RFC 9116 and the serving rules from the host’s own docs; the order and the two-person routing are my working rules.
- 01 Create the address: a security@ alias or group on the company domain.
- 02 Route it to two people, with spam filtering relaxed for that address.
- 03 Write the one-page disclosure policy and publish it at an https URL.
- 04 Write the file: Contact, Expires, Policy, Preferred-Languages and Canonical.
- 05 Serve it at /.well-known/security.txt over HTTPS on every domain the product uses.
- 06 Send a test report from a mailbox outside the company and time the human reply.
- 07 Put the Expires reminder in a calendar with a named owner.
For step 7, my working rule is a reminder about a month before the Expires date, owned by one named person rather than a shared calendar.
The inbox: a security address that reaches two people
A disclosure inbox, by my working rule, is an alias that reaches two people, with spam filtering relaxed and no ticket form in the way. A report that gets an automatic reply and nothing else is, to the person who sent it, the same as no contact at all.
Use security@yourdomain as an alias or a group, never one person’s mailbox, never a no-reply address, and never a ticketing address that answers with a form. RFC 9116 says security email addresses should follow the conventions of RFC 2142, which lists the SECURITY mailbox under network operations for “Security bulletins or queries”. Two recipients is my working rule, so one person’s holiday does not stop the mail.
Relax spam filtering for that address, because a genuine report can carry attachments, links and pasted request logs that a filter may score badly. An automatic acknowledgment is fine only if a person follows it. The address belongs to the company’s own domain account, not a contractor’s; keeping an inventory of which accounts the company owns is its own job. A web form is an acceptable second Contact line, never the only one: a form behind a login or a CAPTCHA fails the person trying to report.
Serving the file: the path, the content type and one file per domain
The path is /.well-known/security.txt, fetched over https, with a Content-Type of text/plain and the charset set to utf-8. For legacy compatibility, the RFC says a file might also be placed at the top-level /security.txt, or that path might redirect to the /.well-known/ one, and when a file is present in both places the /.well-known/ one must be used.
Scope is per host. A file applies only to the domain or IP address in the URL used to fetch it, not to its subdomains or parent domains, though it may also apply to the organization’s products and services. CISA’s post says each domain and subdomain should have its own file. So the app, the marketing site and the API host each serve one, and each file’s Canonical line lists that file’s own URL, because the RFC says a file fetched from a URL not listed in its canonical fields should not be trusted.
On a Next.js build, Next.js’s public folder docs say files in public are served from the base URL, so public/.well-known/security.txt is the place to put it. Whether a dot-folder in public is served as is on Vercel is not stated in the Next.js docs, so the first verify check below settles it on your own deploy. A host or CDN may manage the file for you: Cloudflare’s security.txt setting sits under Security, then Settings, filtered by Web application exploits, and marks Contact and Expires as required.
The traps, in my reading, are routing ones: a single-page app’s catch-all route answering the path with the app’s HTML and a success status, an auth middleware redirecting it to the login page, and a www to apex redirect that drops the path. The response headers that travel with this file belong with the rest of the production security headers, including the content security policy unsafe-inline fix.
A vulnerability disclosure policy template for a small team
A vulnerability disclosure policy for a small team is 7 short parts: what is in scope, what is not, how to report, what you promise, authorization for good-faith research, no payment, and a date. Promise an acknowledgment time you can keep, never a fix time.
In practice, the vulnerability disclosure policy is the page the Policy: line points to, which RFC 9116 says can help researchers understand the organization’s vulnerability reporting practices. CISA draws the line the same way: a vulnerability disclosure process “simply establishes how an organization receives reports, what is in scope, and what reporters can expect.” A vulnerability disclosure program, in my reading, is that policy plus the people who answer the mail.
Responsible disclosure is another name you will meet for the same arrangement, so the template below works as a responsible disclosure policy template too. The CERT Coordination Center’s guide explains why it prefers the other term: what counts as responsible “is a matter of opinion”, so CERT/CC advocates the term Coordinated Vulnerability Disclosure “to reduce misunderstanding and promote cooperation.” Responsible disclosure programs and coordinated ones, as far as a small company is concerned, ask for the same page.
The seven parts below are my cut of the headings in CISA’s vulnerability disclosure policy template, whose sections include “Introduction”, “Authorization”, “Scope”, “Reporting a vulnerability” and “What you can expect from us”. The authorization paragraph keeps CISA’s wording, which CISA strongly encourages keeping as is.
Security disclosure policy for example.com
1. In scope: example.com, app.example.com and api.example.com.
2. Out of scope: services run by third parties we use; denial of service;
social engineering or phishing; reading or changing other users' data
beyond what shows the problem.
3. How to report: email security@example.com with the URL, the steps to
reproduce, the account you used, and the impact you saw.
4. What we promise: an acknowledgment within [N] working days, a status
update when we know more, and credit if you want it.
5. Authorization: If you make a good faith effort to comply with this policy
during your security research, we will consider your research to be
authorized, we will work with you to understand and resolve the issue
quickly, and [Company] will not recommend or pursue legal action related
to your research.
6. Payment: we do not pay for reports.
7. Date: last updated [date]. Questions to security@example.com.
Why the authorization part matters: RFC 9116 says researchers shouldn’t assume that the presence or absence of a security.txt file grants or denies permission for security testing, and that any such permission may be stated in the disclosure policy or a new field. disclose.io’s open-source terms are a second starting text, public domain under CC0, with a standalone safe harbor clause you can attach to an existing policy. The authorization part is legal text: this is not legal advice, and a lawyer should read it before you publish it. CISA’s template was written for US federal agencies, so treat it as an outline, not a standard your company has to meet.
When the first report arrives
My working order for a first report, a rule of thumb rather than a standard:
- 01 Acknowledge it within one working day, from a person, not only the auto-reply.
- 02 Reproduce it on a system you own before anything else.
- 03 Rate it by what data or money it reaches, not by the words in the report.
- 04 Fix it, then ask the reporter to confirm the fix.
- 05 Credit the reporter if they want it.
A report that asks for payment for a missing header or a public robots.txt gets one polite answer quoting the policy’s no-payment line. Open attachments with care: RFC 9116 says reports received because of the file may be hostile, malformed, or malicious.
If the report shows real access to customer data, it is an incident, and the plan is an incident response plan template. A report about reading server files through a crafted path points at remote file inclusion and path traversal. A confirmed report joins the rest of your findings, and tracking and ordering them is the job of vulnerability management tools for a small team.
What is a bug bounty program, and when does one make sense?
A bug bounty program pays outside researchers for valid vulnerability reports under published rules. For a small app it comes third, after a disclosure contact and a review of your own code: a public bounty on an app nobody has reviewed tends to pay for findings a review would have caught first.
Google calls its own a Vulnerability Reward Program, running for its web properties since November 2010, and Google’s reward program rules set out what qualifies and a reward table. Vulnerability reward programs and bug bounties are the same thing under two names. Responsible disclosure rewards are the thanks a policy can offer without a bounty: credit, a listing on an acknowledgments page, sometimes money, decided case by case. disclose.io’s terms state that incentives or bounties “are not a prerequisite” for a program to count as a vulnerability disclosure program.
| Option | Who reports | What you pay | What you need first |
|---|---|---|---|
| Disclosure contact | Anyone who finds something | Nothing | An inbox two people read and a one-page policy |
| Private bounty | Researchers you invite | A published reward table | Known problems fixed and someone to triage every week |
| Public bounty | Anyone who accepts the rules | A published reward table | All of the above, a budget and a triage rota |
My ruling: a disclosure contact first, always. A bounty only when the known problems are fixed, someone can triage reports every week, and there is a budget, because an unreviewed app hands outsiders the easy findings, and each one costs a reward. A targeted penetration test is the step between the two. Bounty platforms such as HackerOne and Bugcrowd sell and run bounty programs for companies; how a bounty compares with a test in detail is covered in pen test vs vulnerability assessment, and where a bug bounty fits.
How to verify it
A disclosure contact is verified by 5 checks: the path returns plain text in a private window, curl shows 200, text/plain and a Canonical line naming that URL, Expires is in the future, a test report from an outside mailbox gets a human reply, and the policy opens logged out.
Run these against your own domains to verify the security.txt file is reachable where researchers will look. The expected results come from RFC 9116’s location and field rules; the test report and the timing are the part no document checks for you.
- 01 Browser first: open https://yourdomain/.well-known/security.txt in a private window. You should see the fields as plain text, not a page from your app.
- 02 Headers and body: run the two curl commands below. You should see status 200, no redirect to a login, a content-type of text/plain with charset=utf-8, and a Canonical line naming the URL you fetched.
- 03 Expires: the date is in the future and under a year away, and the calendar reminder exists with a named owner.
- 04 Test report: from a mailbox outside the company, send a short report with an attachment and a link. Record the time you sent it and the time a person, not the auto-reply, answered.
- 05 Policy: open the Policy URL logged out. You should see the policy, not a login page.
curl -sI https://example.com/.well-known/security.txt
curl -s https://example.com/.well-known/security.txt
Repeat checks 1 and 2 on every domain that serves the product, since each file covers only its own host. Keep the evidence: the curl output with the date, the test message and the reply with both timestamps, and the calendar entry. A file that returns the app’s HTML with a success status fails the browser check, even though an uptime monitor would pass it.
In the Production Hardening Sprint, deliverable 3.9 is verified this way: check the published file, contact details, and delivery of a test message.
Where the sprint does this
Deliverable 3.9 of the Production Hardening Sprint publishes security.txt and a monitored disclosure contact for vulnerability reports, and its check is the one set out under the verify heading above. Targeted external penetration testing is a separate deliverable, 3.10. Both are recorded in deliverable 13.1, the production readiness report, which accounts for all 123 IDs, keeps failures visible until resolved and explains genuine non-applicable items. Formal third-party certifications and independent audit opinions are separate from the sprint deliverables. Every item is listed in the published scope.
Common questions about disclosure contacts and bug bounties
What is the NIST’s policy on vulnerability disclosure?
NIST’s guidance is NIST SP 800-216, “Recommendations for Federal Vulnerability Disclosure Guidelines”, published in May 2023 for software, hardware and digital services under US federal control. It recommends guidance for setting up a federal disclosure framework, handling vulnerability reports properly, and communicating the mitigation or remediation. In my reading it works as an outline for any company, though a two-person team needs only the intake part.
Is a bug bounty legal or illegal?
A bug bounty is legal when the testing stays inside the permission its published rules give; testing outside that permission may fall under anti-hacking laws, depending on the jurisdiction. Do not look to the security.txt file for the answer: RFC 9116 tells researchers not to read permission, or a refusal, into whether the file is there, and says any permission may be stated in the disclosure policy. That is what the authorization part of a policy, or a bounty’s rules, is for, and disclose.io’s safe harbor terms list authorization against anti-hacking laws among their core requirements. This is not legal advice.
How do I write a bug report?
A useful security bug report gives the URL, the steps to reproduce, the account used, the impact, and no customer data beyond what shows the problem. CISA’s template asks reporters to describe where the vulnerability was found, its potential impact, and the steps needed to reproduce it. Put the same list in your own policy’s “how to report” part, so reports arrive in a shape you can act on.
Can you provide a list of vulnerability disclosure programs?
No list is printed here: each organization publishes its own policy, and its security.txt Policy line is the place to find it. At national level, CISA’s coordinated vulnerability disclosure program coordinates the identification, remediation and disclosure of vulnerabilities that pose risks across critical infrastructure and other essential technologies, and takes reports through its VINCE-NT platform. disclose.io keeps a public directory of programs as well.
How much does a bug bounty pay?
A bug bounty pays what its published reward table says, and a disclosure policy with no bounty pays nothing and says so. Google’s reward table for its web properties, read on September 30, 2026, runs from $100 to $101,010 depending on the domain and the kind of bug, adjusted by a report-quality factor, and the final amount is always chosen at the discretion of its reward panel.
The checks in this guide show you where the app is open. The sprint below closes those gaps, tests the result and writes the evidence down.
Built it with AI. Now it has to hold up for real customers.
The Production Hardening Sprint takes the app you already have and builds the production foundation underneath it. Authentication and access rules, payments that stay consistent, error handling, monitoring, backups, automated tests and a documented handover. Our engineers work inside your existing codebase for ten working days. All 123 deliverables are included, and you get the evidence for each one.
See the Production Hardening Sprint →
$2,500 fixed price · 10 working days · One codebase