Transport security is the cheapest four points in the trust pillar, and the ones most often dropped by a site that has already done the hard part. The certificate is valid, the redirect from http to https works, and the report still shows three out of four, because nobody ever added a Strict-Transport-Security header, or because a template somewhere still references a stylesheet over http://. Check D1 is three facts about one homepage fetch. This post says exactly what each fact is, how the scanner establishes it, and what we do on our own site to keep all three true.
What D1 measures
D1 is "Transport security", worth 4 of the 25 points in pillar D and rated Low effort. The rubric's one-line definition is "Valid TLS, an HSTS header, and no mixed content under render." The engine turns that into three parts with fixed weights.
| Part | Points | What earns it |
|---|---|---|
| Valid TLS | 2 | The crawler's HTTPS fetch of the homepage completed without a TLS error |
| HSTS | 1 | The homepage response carried a Strict-Transport-Security header |
| No mixed content | 1 | No src or href attribute in the page begins with http:// |
Three points is the score of a site that got everything right except the header. It is also the score of a site with the header and one stray http:// image. The evidence block on the report records the three values separately, as tlsValid, hsts and mixedContentReferences, so you can see which one you dropped without guessing.
Valid TLS
The scanner does not run a separate certificate audit. It fetches the homepage over HTTPS the way any client would, and if the connection fails at the TLS layer, because the certificate has expired, does not cover the hostname, or sits on a chain the client cannot build, there is no page, and the two points are gone. This is deliberate. A certificate that fails validation in a standard client fails for an agent in exactly the same way, and the agent has nothing to read. The two-point weight reflects that: it is the only part of D1 that takes the rest of the scan down with it.
The hostname point deserves a sentence. If your canonical host is www.example.com but the certificate only covers example.com, or the other way round, then whichever host you redirect from fails before the redirect can happen. Canonical hygiene covers the four host variants the scanner probes; the certificate has to cover every one of them.
The HSTS header
The second point is the simplest test in the rubric. The crawler looks at the headers on the homepage response, and if Strict-Transport-Security is present, the point is earned. The engine does not, as of this version, parse the value, so a max-age of one second would pass. MDN's page on Strict-Transport-Security covers the directives and what browsers do with them.
The rubric does not want the header for the browser behaviour, which agents largely do not share. It wants it because the header is a statement of intent in a machine-readable place: the operator has decided the site is HTTPS-only and said so on every response. A program deciding whether a page is what it claims to be can check that in one request, which is the whole premise of pillar D.
Mixed content under render
The third point is where the "under render" in the title comes in. If the scan performed a headless render of the homepage, D1 runs its test against the rendered HTML. Otherwise it uses the raw response body. The test is a pattern match: every src="http://…" and href="http://…" in the document is counted, with localhost and 127. addresses excluded. A count of zero earns the point, and the count itself is published as mixedContentReferences.
Two consequences follow from that being the test.
First, a script that writes an http:// image or stylesheet into the page after load counts, because the render captures the DOM after your JavaScript has run. A raw HTML file that is clean can still fail if a widget, a tag manager or a legacy embed injects insecure references. If your report shows a non-zero count and you cannot find it in your templates, look at what runs on the client.
Second, the pattern does not distinguish a subresource from a link. A plain anchor pointing at a partner's http:// site is counted alongside an http:// script tag. A browser treats the two very differently, and a strict reading of "mixed content" would only count the subresource. The engine's test is the broader one, so the practical fix is to remove every http:// reference from the page, links included.
A Content-Security-Policy does not rescue you here. A default-src 'self' policy stops the browser loading the insecure resource, which is good for the visitor, but the reference is still in the HTML and the scanner still counts it. The same goes for upgrade-insecure-requests: it changes what the browser fetches, not what the document says.
How we set ours
Our site is a Next.js application with nothing in front of the origin, so the application sets its own headers. The relevant entry in next.config.ts is applied to every path:
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
}
max-age=63072000 is two years in seconds. includeSubDomains extends the policy to every subdomain of the host, which is only safe if every subdomain actually serves HTTPS; we have none that do not. preload signals that we are willing to be added to the HSTS list built into browsers, maintained at hstspreload.org. As of this writing that list requires all three directives and a max-age of at least a year. Preloading means a browser refuses plain HTTP for the host even on its very first visit, before it has ever seen the header. It is also slow to reverse, so read the removal conditions on that page before you submit.
The same header list sets a Content-Security-Policy. Ours is locked to 'self' for everything except the analytics host and the inline script Next uses for hydration:
default-src 'self'; script-src 'self' 'unsafe-inline' https://plausible.io;
style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self';
connect-src 'self' https://plausible.io; form-action 'self';
frame-ancestors 'none'; base-uri 'self'; object-src 'none'
The CSP is not what earns the D1 point. It is what keeps the mixed content count at zero over time. With img-src 'self' data: and script-src limited to two hosts, a stray http:// reference shows up as a blocked-resource error in the browser console on the first page load, rather than as a lower score at the next scan. MDN's Content-Security-Policy reference is the place to start if you are writing one.
The fixes, in order
All three parts of D1 sit in the hours tier of the three tiers of fixes. None needs an architecture change.
- Certificate. Renew it, serve the full chain, and make sure it covers every host you redirect from as well as the one you redirect to. Most hosting platforms do this automatically; the failures we see are on self-managed servers where the renewal job stopped.
- Header. One line at whatever terminates TLS. In an application, it is a response header on every route. In nginx, it is an
add_header. On a hosting platform, it is usually a setting. Start with a shortmax-ageif you are unsure every subdomain is ready, then raise it. - Mixed content. Search the built output, not the source, because the offending reference is often in a shared partial or a third-party snippet.
Two commands cover the checks you can run from a terminal:
curl -sI https://example.com/ | grep -i strict-transport-security
curl -s https://example.com/ | grep -oE '(src|href)="http://[^"]*"' | sort | uniq -c
The second only sees the raw HTML. If the scan reports references that the grep does not find, they are being written by JavaScript, and the render is showing you what the raw fetch cannot.
Check your own site
Run the free scan and read the D1 evidence: tlsValid, hsts and mixedContentReferences, each one a fact the crawl captured. The definition sits with the rest of the trust pillar on the methodology page, and our own D1 result is public on our live report.