Skip to content

security.txt Checker

How the security.txt checker fetches /.well-known/security.txt or reads a pasted file, every RFC 9116 rule it applies, what each finding means for a researcher, and how to fix and verify it.

Open Markdown

The tool page is tamperbox.dev/security-txt-checker. It opens by checking google.com live, answers in one sentence, and shows the file the way a security researcher reads it. This page is the long version: what our server requests, every rule the page applies with the section of RFC 9116 behind it, what each finding means, and how to fix and confirm it.

Where processing happens: a host name is sent to the Tamperbox server, which requests https://host/.well-known/security.txt once, follows up to five HTTPS redirects, reads at most 64 KiB and returns the file with its status and media type. A pasted or dropped file is read in your browser and never uploaded. The same parser judges both. History and tool-to-tool transfers remain in the browser; see Browser transfers below.

The questions come in the order a researcher who just found a vulnerability would ask them:

  1. Is there a file where the standard says to look? The address, the HTTP status, the media type, and whether the answer is a text file or the site’s web page.
  2. Is it valid under RFC 9116? A Contact, exactly one Expires, well-formed lines and absolute URIs.
  3. Where do I send the report, and how? The contacts in order of preference, the key to encrypt with, the policy to read first, and the languages accepted.
  4. Is it still current? The Expires date against the moment of the check and the one-year recommendation.
  5. Is this the official copy? Whether a Canonical line names the address the file was actually served from.
  6. What should the file say instead? A corrected file, or, when there is no file, a short form that writes one.

example.com or www.example.com. The check runs when you stop typing, or on Enter. The page refuses, with a message under the field, a value with spaces, a scheme other than http or https, an IP address, a host without a top-level domain, a port other than 80 or 443 and credentials in an address; the last two offer a repair. Our server refuses private hosts and addresses with “Use a public domain name, not a private host or IP address.”

Text is read as a file when it has a line break, or when its first line looks like a field (Contact: …), a comment (#) or a PGP header, and does not start with http:// or https://. A dropped file is read the same way, up to 64 KiB. A missing final line break is added before checking, because clipboards usually drop it.

You paste The field keeps Why
https://github.com/oceanrescue/ github.com A full address is reduced to its host by the shared paste cleanup
security@example.com or mailto: example.com The domain of an email address
"example.com" or <example.com> example.com Quotes, brackets and trailing punctuation dropped
A security.txt file The file, unchanged It is read locally
Text that fits nothing The text as pasted The parser’s own error explains it

Cleaning runs on paste, drop, handoff, Enter and leaving the field; it never rewrites what you type, and never touches Break it or Heal it loads.

The verdict names the outcome first: “Valid security.txt.”, “Not valid:” followed by the worst problem, or “No security.txt.” when the address did not return a text file. The detail line says where the file came from, including the number of redirects. Chips count problems and things to check, the contacts, and the days to expiry; each scrolls to its evidence. Two privacy items say where this input went.

Directly under the verdict, worst first. Errors make the file invalid; warnings do not. A finding tied to a line has a Show line button that highlights it in the file listing.

The fields in the order a researcher uses them: Report to, Encrypt with, Read first, Thanks, Languages (with names), Canonical, Hiring, Advisories and Other fields. A missing field says what its absence means. Every value has a Copy button; a value on an error line carries a Problem badge.

The Expires date on a time axis with the moment of the check and the one-year mark. The file as served, numbered, with problem lines marked. Under it, the corrected file to copy or download, or the draft form when there was no file.

History logs every Break it and Heal it load with Revert and Reset input. Take it further carries values from the file into other security tools: the host serving the Encryption key to the certificate checker, the first https Contact to the security headers checker, the first https Policy to the mixed content checker, and, after a redirect, the host that served the file to the certificate checker. Reserved example names are never offered.

Explicit tool links temporarily place the transferred input in browser sessionStorage. The URL fragment carries a random token, not the input. The receiving page accepts that token once, for its destination path and for less than one minute, then removes it from the address. A same-origin browser channel can complete a transfer into another tab. Ordinary documentation links do not transfer input. When browser storage is unavailable, navigation still works but the input may need to be pasted again.

One GET https://host/.well-known/security.txt, the location RFC 9116 section 3 defines: for web-based services the file MUST be placed under the /.well-known/ path. Redirects are followed up to five hops; each target must be https://, a loop stops the check, and a sixth redirect stops it with “Too many security.txt redirects.” The body is read up to 64 KiB and decoded as strict UTF-8. The legacy /security.txt at the root, which section 3 allows only for legacy compatibility, as a copy or as a redirect to the /.well-known/ file, is not requested.

Rule Finding Severity Source
Final status is 200 Not served Error RFC 9116 section 3
Content-Type is text/plain, and a charset, if present, is utf-8 Wrong media type Error section 3
Body is valid UTF-8 Not UTF-8 Error section 4
Body fits in 64 KiB Larger than 64 KiB Error Tool limit

Section 3 states the file “MUST have a Content-Type of ‘text/plain’ with the default charset parameter set to ‘utf-8’”. When the status is not 200, or the answer is HTML, the page treats the site as having no file and shows the draft form instead of a field report.

Blank lines and lines starting with # are skipped (section 2.1). Every other line must be a field name, a colon, a space and a non-empty value without control characters, as in the ABNF of section 4; otherwise it is “Not a field line”. Field names are case-insensitive (section 2). A fetched file must end with a line break, CRLF or LF (section 2.2; “Last line has no line break”). Unknown fields are listed without an error, as section 2.4 allows extensions.

Rule Finding Source
At least one Contact No way to report section 2.5.3: “This field MUST always be present”
Exactly one Expires No Expires date / Expires appears more than once section 2.5.5: “MUST always be present and MUST NOT appear more than once”
Expires is RFC 3339 Expires is not a date section 2.5.5, RFC 3339 section 5.6
Expires is in the future The file has expired (error) section 2.5.5
Expires at most 366 days ahead Expires more than a year ahead (warning) section 2.5.5: a value less than a year ahead is RECOMMENDED
URIs are absolute Not an absolute URI section 2.5.3, section 4, RFC 3986 section 4.3
Web URIs use https A web address uses http:// section 2.5.3 and siblings: “MUST begin with ‘https://’”
One Preferred-Languages Preferred-Languages repeated section 2.5.8
Valid language tags Not a language tag section 2.5.8, RFC 5646
A Canonical matches the fetched URL Canonical points elsewhere section 2.5.2

The URI rule applies to Contact, Canonical, Encryption, Policy, Hiring and Acknowledgments. Dates are checked against the calendar, so 2027-02-30T00:00:00Z and a bare 2027-03-01 both fail. Canonical is only compared for a fetched file.

A file starting with -----BEGIN PGP SIGNED MESSAGE----- is split from its signature and dash-escaping is removed, as RFC 9580 section 7 describes the cleartext framework. A malformed envelope is “Broken signature block” (error). A well-formed one is “Signed, not verified here” (warning): RFC 9116 section 2.3 RECOMMENDS a digital signature, and verifying the signature is the reader’s job.

Each repair rewrites the file and checks it again: set Expires to 364 days after the check at midnight UTC; switch http:// to https:// in web fields; turn broken lines into comments; keep the first Preferred-Languages; point Canonical at the fetched URL; end the last line. A missing Contact is never invented. For a fetched file, a repair loads a corrected copy here; nothing changes on the site.

RFC 9116: A File Format to Aid in Security Vulnerability Disclosure

Section titled “RFC 9116: A File Format to Aid in Security Vulnerability Disclosure”

Implemented: the location and media type of section 3; comments, line separators and the field rules of sections 2.1 to 2.5 for Contact, Expires, Encryption, Policy, Acknowledgments, Preferred-Languages, Canonical and Hiring; the ABNF of section 4 as one regular expression. All rules run in public/lib/security-txt.js, shared by the browser and the server, so a pasted file and a fetched one get the same report; the response rules run on the server only. CSAF lines (registered later in the IANA registry) are listed but not validated.

Where the tool is stricter or more lenient

Section titled “Where the tool is stricter or more lenient”
Point RFC text Tool
Space after the colon The ABNF of section 4 requires it (fs SP) Requires the space and reports Name:value as a broken line
Expires a year ahead Less than a year is RECOMMENDED Warning after 366 days, never an error
Signature Signing is RECOMMENDED Absence is not a finding; presence is not verified
Canonical Lists every URI the file is served from Checked only against the final fetched URL
Legacy location /security.txt may redirect Not requested
Size No limit stated 64 KiB read limit

The space rule follows the ABNF, so that the most common hand-written slip, Contact:mailto:…, is caught before a lenient reader guesses.

Expires follows the date-time production of RFC 3339 section 5.6, including fractional seconds and offsets. Language tags are checked with the browser’s Intl.getCanonicalLocales, which implements RFC 5646 (BCP 47). URIs must be absolute in the sense of RFC 3986 section 4.3 and parse with the WHATWG URL parser.

Finding Severity Section What it means Fix
Not served Error 3 No file where readers look Publish one at /.well-known/security.txt
Wrong media type Error 3 Strict readers refuse it; HTML means the app answers every path Serve text/plain; charset=utf-8
Not UTF-8 Error 4 Garbled characters Save as UTF-8
Larger than 64 KiB Error Tool limit Not a security.txt Replace what is served
Finding Severity Section What it means Fix
No way to report Error 2.5.3 Nowhere to send a report Add a Contact
No Expires date / appears more than once Error 2.5.5 Readers may ignore the file Keep one Expires
Expires is not a date Error 2.5.5 No expiry can be read Use RFC 3339
The file has expired Error 2.5.5 Contacts treated as stale Review, re-date
Expires more than a year ahead Warning 2.5.5 Valid, reviewed too rarely Date it within a year
A web address uses http:// Error 2.5.3 Can be read or replaced in transit Use https://
Not an absolute URI Error 4 Readers cannot follow it Add the scheme
Not a field line Error 4 Line skipped Name: value or #
Preferred-Languages repeated Error 2.5.8 Readers take one line One comma list
Not a language tag Error 2.5.8 Languages unclear BCP 47 tags
Canonical points elsewhere Error 2.5.2 Copy cannot be told current Name the served URL
Last line has no line break Error 2.2 Last field may be dropped End with a newline
Finding Severity Section What it means Fix
Broken signature block Error RFC 9580 section 7 Fields unreliable Re-sign or publish unsigned
Signed, not verified here Warning 2.3 Signature not checked Verify with gpg --verify
  1. Fill in the draft form: Contact is required, Expires is filled with a date 364 days ahead, Canonical is set to the requested address.
  2. Download the file and publish it at /.well-known/security.txt.
  3. On a single-page app, add a static route for that path before the catch-all, and map it to text/plain; charset=utf-8.

Confirm outside Tamperbox:

Terminal window
curl -sSI https://example.com/.well-known/security.txt
curl -sS https://example.com/.well-known/security.txt

The first must show HTTP/2 200 (or HTTP/1.1 200) and content-type: text/plain; charset=utf-8.

Review every Contact and Policy first, then set one Expires less than a year ahead, for example with date -u -d '+364 days' +%Y-%m-%dT00:00:00Z on GNU systems. Put the next review in the same calendar as certificate and domain renewals.

Usually www against the bare domain, or a file copied from another site. List every address the file is served at, one Canonical each, or point the only Canonical at the address the redirects end on. Heal it writes the fetched URL.

Edit the plain text, then sign it again with gpg --clearsign security.txt and publish the .asc output as security.txt. Every repair here removes the signature, because the signed text changes.

Case What it loads Finding
Expired yesterday Your file with Expires set to yesterday The file has expired
No Contact Your file without Contact lines No way to report
Expires twice A second Expires line Expires appears more than once
Contact over http A web field switched to http:// A web address uses http://
No space after a colon Name:value on one line Not a field line
Served as a web page A sample host answering with HTML No security.txt
No security.txt A sample host answering 404 Not served

The first five build on your own file when you checked one; the last two use reserved harbor-supply.example hosts answered locally.

The page opens on google.com, checked live, because among the candidates compared it shows the most of the tool: a redirect, several fields and one instructive warning. The file belongs to Google and can change at any time, so read the result on the page rather than the details here, which describe the file as it was when this page was written.

The request to https://google.com/.well-known/security.txt is redirected once to the www host, and the file found there is read field by field:

Part What the page shows Finding
Redirect “after 1 redirect” in the verdict’s detail line None: the redirect stays on https://
Contact A web form first, then a mailto: address, in order of preference None
Encryption, Policy, Acknowledgments, Hiring Listed under Encrypt with, Read first, Thanks and Hiring None
Canonical “Not set” None: Canonical is optional
Expires A date several years ahead on the time axis, past the one-year mark Expires more than a year ahead (warning)

The verdict is “Valid security.txt.” with a warning chip, because the only finding is a recommendation, not an error. Heal it offers “Set Expires to” a date 364 days after the check, and the corrected file appears under the listing. Take it further offers the certificate of the host serving the key, the security headers of the report page, mixed content on the policy page and the certificate of the host the redirect ended on.

tamperbox.dev and gmail.com publish no file; checking either shows “No security.txt.” and the draft form, with Expires and Canonical filled in. The Break it cases show every error on a copy of the file you checked.

The Tamperbox MCP server exposes search and execute. Call POST /v1/tools/security_txt_checker with { "domain": "example.com" } through tamperbox.request to fetch and check a live file; the result carries status, report (fields, contacts, expires, signature, findings, valid), redirects and truncated. For a file you already have, POST /v1/tools/security_txt_analyze parses it without any network request and can apply named repairs. Apply Method to read the findings, and keep these warnings with any conclusion:

  • valid: true is syntax and rules, not evidence that the Contact is monitored.
  • signature: "unverified" means a signature was found, not that it is genuine.
  • A Contact URL is never fetched; the check does not test any vulnerability.

See Connect an MCP client and Code mode for the calling pattern.

  • One request from one server location at one moment, to /.well-known/security.txt only; the legacy /security.txt is not requested.
  • OpenPGP signatures are separated but not verified.
  • A valid file does not show who controls the contacts, that anyone reads them, or that testing is permitted; read the Policy.
  • A pasted file has no status, media type, redirects or fetch URL, so those rules and the Canonical comparison need a live check.
  • CSAF and other extension fields are listed but not validated.
  • History is a browser interaction record, not server monitoring. Explicit tool links use temporary browser storage as described in Browser transfers.