Each Monday, checks the sites you list for the browser protections they should switch on, and emails what is missing and how to fix it.
Uses
Gmail
OpenAI
Check your websites' security settings every week
Every Monday at 08:00, each website you list gets one ordinary page request, exactly what a visitor's browser makes, and only that response is read. Nothing is probed, no paths are guessed and no address other than the ones you list is contacted, so it is a passive check of sites you own, not a scanner.
The email you get names, for each site, which of Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options and Referrer-Policy are missing, whether a visitor arriving over plain http is sent on to https, and the server banner verbatim. AI then explains what each missing header does and the change to make in your web server or CDN, and is told not to judge how exploitable anything is or invent findings. The readings go out even if the write-up fails. A site that cannot be reached is listed separately with the reason and never stops the rest being checked.
Put only sites you are responsible for in the list, since each one is requested every week.
siteList to blank.Set siteList to blank.reportEmail to blank.Set reportEmail to blank.siteList with
replaced by "Split siteList with
replaced by
with , replaced by
by a new line and save it as siteEntries.siteReports to an empty list.Set siteReports to an empty list.unreachable to an empty list.Set unreachable to an empty list.sitesWithGaps to 0.Set sitesWithGaps to 0.siteEntry in siteEntries:For each siteEntry in siteEntries:siteEntry and save it as entry.Trim siteEntry and save it as entry.entry is emptyIf entry is emptyhttp:// with in entry with https:// replaced by (ignoring case) (ignoring case) and save it as siteHost.Replace http:// with in entry with https:// replaced by (ignoring case) (ignoring case) and save it as siteHost.siteHost is emptyIf siteHost is emptycheckUrl to http://@siteHost.Set checkUrl to http://@siteHost.reached to false.Set reached to false.finalUrl to blank.Set finalUrl to blank.responseStatus to 0.Set responseStatus to 0.responseHeaders to an empty record.Set responseHeaders to an empty record.fetchErrorIf a step below fails, save the error as fetchErrorcheckUrl and save it as response.Fetch checkUrl and save it as response.response.url and save it as finalUrl.Trim response.url and save it as finalUrl.responseStatus to response.status.Change responseStatus to response.status.responseHeaders to response.headers.Change responseHeaders to response.headers.reached to true.Change reached to true.checkUrl with the site address in fetchError.message and save it as safeReason.Replace checkUrl with the site address in fetchError.message and save it as safeReason.siteHost - could not be reached: safeReason to the end of unreachable and save it as unreachable.Add siteHost - could not be reached: safeReason to the end of unreachable and save it as unreachable.reached isn't yesIf reached isn't yesmissingHeaders to an empty list.Set missingHeaders to an empty list.responseHeaders["strict-transport-security"] is emptyIf responseHeaders["strict-transport-security"] is emptyStrict-Transport-Security to the end of missingHeaders and save it as missingHeaders.Add Strict-Transport-Security to the end of missingHeaders and save it as missingHeaders.responseHeaders["content-security-policy"] is emptyIf responseHeaders["content-security-policy"] is emptyContent-Security-Policy to the end of missingHeaders and save it as missingHeaders.Add Content-Security-Policy to the end of missingHeaders and save it as missingHeaders.responseHeaders["x-frame-options"] is emptyIf responseHeaders["x-frame-options"] is emptyX-Frame-Options to the end of missingHeaders and save it as missingHeaders.Add X-Frame-Options to the end of missingHeaders and save it as missingHeaders.responseHeaders["x-content-type-options"] is emptyIf responseHeaders["x-content-type-options"] is emptyX-Content-Type-Options to the end of missingHeaders and save it as missingHeaders.Add X-Content-Type-Options to the end of missingHeaders and save it as missingHeaders.responseHeaders["referrer-policy"] is emptyIf responseHeaders["referrer-policy"] is emptyReferrer-Policy to the end of missingHeaders and save it as missingHeaders.Add Referrer-Policy to the end of missingHeaders and save it as missingHeaders.finalUrl starts with https:// (ignoring case) and save it as redirectsToHttps.Check whether finalUrl starts with https:// (ignoring case) and save it as redirectsToHttps.httpsLine to HTTPS: the plain http address is sent on to finalUrl.Set httpsLine to HTTPS: the plain http address is sent on to finalUrl.redirectsToHttps isn't yesIf redirectsToHttps isn't yeshttpsLine to HTTPS: the plain http address was NOT sent on to https - it ended at finalUrl.Change httpsLine to HTTPS: the plain http address was NOT sent on to https - it ended at finalUrl.headerLine to Security headers: all five present.Set headerLine to Security headers: all five present.missingHeaders isn't emptyIf missingHeaders isn't emptyheaderLine to Missing security headers: missingHeaders joined by , .Change headerLine to Missing security headers: missingHeaders joined by , .serverBanner to responseHeaders.server.Set serverBanner to responseHeaders.server.serverLine to Server header: not sent.Set serverLine to Server header: not sent.serverBanner isn't emptyIf serverBanner isn't emptyserverLine to Server header: serverBanner.Change serverLine to Server header: serverBanner.missingHeaders isn't empty or redirectsToHttps isn't yesIf missingHeaders isn't empty or redirectsToHttps isn't yessitesWithGaps.Add 1 to sitesWithGaps.siteHost (read at finalUrl, status responseStatus)Add siteHost (read at finalUrl, status responseStatus)
httpsLine
headerLine
serverLine to the end of siteReports and save it as siteReports.siteReports is empty and unreachable is emptyIf siteReports is empty and unreachable is emptysiteReports with a blank line and save it as findings.Join siteReports with a blank line and save it as findings.unreachable isn't emptyIf unreachable isn't emptyfindings to findingsChange findings to findings
Could not be checked:
unreachable joined by a new line.explanationText to blank.Set explanationText to blank.explainErrorIf a step below fails, save the error as explainErrorfindings'. Save it as aiAnswer.aiAnswer.response_text and save it as explanationText.Trim aiAnswer.response_text and save it as explanationText.explainError.message.Show in the output: The readings could not be written up, so the email carries them as they are. explainError.message.emailIntro to Checked the length of siteReports of your sites. sitesWithGaps have something missing.Set emailIntro to Checked the length of siteReports of your sites. sitesWithGaps have something missing.
Every site was read with one ordinary page request - the same headers a visitor's browser receives. Nothing was scanned or probed.emailBody to emailIntro plus findings.Set emailBody to emailIntro plus findings.explanationText isn't emptyIf explanationText isn't emptyemailBody to emailIntro@explanationTextChange emailBody to emailIntro@explanationText
---
What each site returned
findings.reportEmail.Send an email to reportEmail.
Subject: Website security headers: sitesWithGaps of the length of siteReports sites have gapsemailBody