Hosting the security.txt with DomainWarn
A security.txt (RFC 9116) tells security researchers where to report a vulnerability: at https://client.example/.well-known/security.txt, with at least one contact and an expiry date (Expires). The expiry date is what orphans most files – once placed, they expire after a year at most, and researchers ignore them.
DomainWarn generates and maintains the file. In the "Website" tab of a domain, under "security.txt", you enter the contacts (email, phone or the HTTPS URL of a report form, at most ten), plus optionally key (Encryption), policy (Policy), acknowledgments (Acknowledgments), job openings (Hiring) and languages (Preferred-Languages). DomainWarn sets Canonical and Expires itself: Canonical is the URL on the client domain, Expires is always 180 days ahead and renewed daily. As long as DomainWarn serves it, the file never expires.
The file lives at a fixed, unguessable URL on the DomainWarn website. On the client domain it is best served directly: the client's web server fetches it there and serves it at /.well-known/security.txt with status 200 (proxy) – researchers see it without a detour at the URL they requested. Where no proxy module is available or only the .htaccess can be edited, the client redirects the path with a 302 instead; RFC 9116 allows retrieval through a redirect. For Apache, nginx and Caddy the dashboard shows ready-made lines for both ways, plus the filter expression for a Cloudflare Redirect Rule; on WordPress and Shopware the plugin or extension takes care of serving it (see below).
# nginx: once in the http context, outside of server
proxy_cache_path /var/cache/nginx/security-txt levels=1 keys_zone=security_txt:1m max_size=1m inactive=1d;
# nginx: in the server block of the domain
location = /.well-known/security.txt {
proxy_pass https://domainwarn.com/security-txt/k7x2…/security.txt;
proxy_ssl_server_name on;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_set_header Host domainwarn.com;
proxy_set_header Cookie "";
proxy_set_header Authorization "";
proxy_hide_header Strict-Transport-Security;
proxy_ignore_headers Cache-Control Expires;
proxy_cache security_txt;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
}
# Alternative without a proxy – Apache: at the very top of the .htaccess, before the WordPress or Shopware rules
RewriteEngine On
RewriteRule ^\.well-known/security\.txt$ https://domainwarn.com/security-txt/k7x2…/security.txt [R=302,L]With a proxy three things matter: the Host header and SNI name domainwarn.com (otherwise the request does not arrive), visitors' cookies and Authorization stay with the client, and DomainWarn's HSTS header is not passed on. A short cache (ten minutes) keeps the load low and bridges an outage of DomainWarn with the last fetched version. The URL stays the same through every change, including switching off and on again – the proxy or redirect is set up once. Switched off, with the domain deleted or the organization suspended, it answers with 404. DomainWarn does not accept values with line breaks or control characters: every value is exactly one line of the file. If the client prefers to place the file themselves, copy it from the dashboard; Expires is then fixed, and the check warns 30 days before it runs out.
If the zone is hosted at Cloudflare and connected through an integration, a click on "Set up with Cloudflare" creates a Worker that serves the file directly on the client domain: https://client.example/.well-known/security.txt answers with 200 and the content of the file, without a redirect. The Worker fetches the file from DomainWarn, keeps it in the Cloudflare cache for an hour and answers with 502 instead of an HTML page if DomainWarn is unreachable. The token needs the additional permissions "Account → Workers Scripts → Edit" (account resources: the account holding the client zones) and "Zone → Workers Routes → Edit" for that. DomainWarn creates a script of its own per domain, domainwarn-security-txt-…, with a route on exactly this path (for the domain and – with a monitored www website – for www) and only ever changes those; foreign Workers and routes stay untouched, a foreign route for the same path is listed in the preview. Workers are included in the Cloudflare free plan, and the route only matches requests for the security.txt.
If the token lacks the Worker permissions, DomainWarn sets up the redirect as a Redirect Rule (Single Redirects, 302) and the card says which permissions are missing; this needs "Zone → Single Redirect → Edit". The rule carries the ref domainwarn_security_txt, and DomainWarn only ever changes that one – all other rules stay as and where they are. Once the Worker permissions are added, a click switches to the Worker and turns the own rule off, because Cloudflare evaluates redirect rules before Workers. When the security.txt is switched off, Worker or rule stay in place and answer with 404. Both only apply to host names served through the Cloudflare proxy (orange cloud). Plain DNS providers (Hetzner DNS, IONOS, netcup, INWX, OVHcloud, Route 53, deSEC, autoDNS) do not answer HTTP; there the web server lines remain the way to go.
The security.txt check then verifies from the outside that the file arrives. Through a redirect it ends up at the DomainWarn URL; served directly, it recognises the file by the comment line # domainwarn-id: …, an ID derived from the token that does not reveal it. A Canonical on the client domain does not count as a mismatch. The card in the dashboard shows the result of the last check: active (directly or through the redirect), no file found yet, a different security.txt – or a stale copy if the file carries our ID but an old Expires, i.e. was copied once and is no longer updated.
See also: What is checked