What is security.txt, and how do you add one to your site?

A security scan flags "no security.txt". What the file is, the two fields it needs, where to put it on Vercel, Netlify or a Vite app, and how to check it works.

· 6 min read

A security scan of your site comes back with a low-priority line: "No security.txt". Nothing is broken, visitors see nothing, and the fix sounds like paperwork. It's also one of the cheapest items on the list: a text file with two lines, about ten minutes of work.

Below: what the file is for, why AI-built sites never have one, how to check yours, a working file for the common hosts, and the two mistakes that make a security.txt look present when it isn't.

What security.txt is for

Sometimes a stranger finds a problem on your site: a Supabase table anyone can read, an API key in the JavaScript bundle, an admin page without a login. Most of the people who find these things want to tell the owner. The question is how. A contact form goes to sales, a support address gets a reply a week later, and many small sites have no contact at all.

security.txt answers that question in a standard place. It's a plain-text file at /.well-known/security.txt, defined in RFC 9116. Researchers, bug bounty platforms and automated tools look there first. If the file exists, the report reaches you. If it doesn't, the person either gives up or posts the problem somewhere public.

The standard requires two fields:

  • Contact: where to send a report. An email (mailto:), a web form (https://) or a phone number (tel:). You can list several, in order of preference.
  • Expires: the date after which the file should no longer be trusted. The RFC recommends a date less than a year ahead, so a forgotten file with a dead address eventually stops being used.

Everything else is optional: Preferred-Languages, Policy (a page that explains how you handle reports), Encryption (a public key for encrypted reports), Acknowledgments, Canonical and Hiring.

security.txt doesn't make a site safer by itself. It doesn't block anything and doesn't hide anything. What it does is shorten the time between "someone found a hole" and "the owner knows about it", which matters most for exactly the kind of site that has no security team.

Why AI-built sites don't have one

Lovable, Bolt, v0, Cursor and the usual Vite or Next.js templates generate pages, components and a database. A security.txt is not a page and nobody asks for it in a prompt, so it's never generated.

There's a second, sneakier reason it goes missing: single-page apps. Most hosts are configured to answer every unknown path with index.html, so client-side routing works. If /.well-known/security.txt doesn't exist as a file, the host happily returns your home page instead, with status 200. A quick check sees "200 OK" and moves on, while a researcher opening the address sees your landing page.

Check it yourself

1. Does the path answer at all?

curl -sI https://yourdomain.com/.well-known/security.txt

Look at two lines in the output:

  • the status: HTTP/2 200 is what you want; 404 means there is no file;
  • content-type: it should be text/plain. If it says text/html, you're looking at the single-page app fallback, not a security.txt.

2. Read what's actually in it

curl -s https://yourdomain.com/.well-known/security.txt

You should see lines like Contact: mailto:... and Expires: .... If you see <!doctype html>, the file isn't deployed and the host is serving your app.

3. Check the date and the address

Two things go stale:

  • Expires in the past means the file is formally invalid, and careful tools ignore it;
  • the Contact address must still be read by someone. Send it a test email and see where it lands.

No terminal? Open https://yourdomain.com/.well-known/security.txt in a browser. A plain page with a few Field: value lines is right. Your normal site design means it's missing.

How to add it

The file

Create a file named security.txt with this content, using your own address and a date about a year from today:

Contact: mailto:security@yourdomain.com
Expires: 2027-11-01T00:00:00.000Z
Preferred-Languages: en

The date format is the one in the example: year, month, day, then a time in UTC. If you don't want a separate mailbox, use the address you read every day. A working personal inbox is better than a polished security@ alias nobody checks.

Where to put it

The file has to end up at /.well-known/security.txt in the deployed site:

  • Vite, Lovable, Bolt, plain React: put it in public/.well-known/security.txt. Everything in public/ is copied as-is into the build.
  • Next.js on Vercel: the same, public/.well-known/security.txt. Files in public/ are served before any rewrites.
  • Netlify: if your build has a public/ folder, use it as above; otherwise place the folder in whatever directory Netlify publishes.
  • Your own server (nginx, a Node server): serve the file as a static path with Content-Type: text/plain; charset=utf-8.

The standard requires the file to be served over HTTPS. The old location, /security.txt in the root, is still accepted for compatibility, but /.well-known/ is the one tools check first. If you have both, keep them identical or make the root one redirect.

After deploying, repeat the two curl commands from the previous section. The folder name starts with a dot, and it's worth confirming that your build step actually copied it.

If you set headers per path (for example in a Netlify _headers file, see how to add security headers on Netlify), there's nothing special to add for this file: the usual headers are fine.

A prompt for your AI tool

Add a security.txt file to this project, following RFC 9116.

1. Create public/.well-known/security.txt (or the equivalent static folder
   for this framework) with:
   Contact: mailto:security@yourdomain.com
   Expires: <a date 11 months from today, in the format 2027-11-01T00:00:00.000Z>
   Preferred-Languages: en
2. Make sure the build copies the .well-known folder into the output and
   that no rewrite or SPA fallback catches /.well-known/security.txt.
3. It must be served as text/plain, not as index.html.
4. Tell me the exact command to verify it after deploy:
   curl -sI https://yourdomain.com/.well-known/security.txt
Do not change anything else.

Replace the address before you send it. Then run the check yourself after the deploy; an AI tool can't see whether your host serves the fallback.

How to keep it from going stale

A security.txt has one built-in failure mode: it expires. The file you add today will be formally invalid in a year, and nobody will tell you.

  • Put the renewal date in your calendar now, a month before Expires.
  • When you change email providers or shut down a mailbox, search the project for security.txt and update Contact.
  • If you have CI, add a step that fails when the Expires date is less than 30 days away. Ten lines of script is enough.

The other way it breaks is quieter: a redesign or a new framework drops the public/.well-known folder, and the path starts returning your app again.

ScanYourSite requests /.well-known/security.txt on every scan, alongside robots.txt and the sitemap, and reports when the path doesn't answer (any status other than 2xx). It's a low-priority finding next to the security headers checks: HSTS, CSP, clickjacking protection, cookie flags and the certificate. The free plan scans one site once a week; Pro scans daily and after every deploy. The scan doesn't validate the file's fields or its Expires date, and it can't tell a real file from an SPA fallback that returns 200, so keep the curl check and the calendar reminder for that.

You can run a free check on your own site at scanyoursite.app/?check=yourdomain.com. To write the file, the securitytxt.org generator fills in the fields for you.

Check your own site in a minute

Free, no sign-up. You get a prioritised list of what’s wrong and a prompt you can paste back into Lovable, Bolt, Cursor or Claude Code.

Free, no sign-up. The basic check makes only the requests an ordinary browser would.

Security headers