What we check

65 checks across 7 categories. Each one below links to a full guide on why it matters and how to fix it.

What a check is

A fixed rule with a stated threshold, not an opinion. Each one names the exact signal it reads, so you can check the same thing by hand and get the same answer. 54 are measured from the page itself; the other 11 come from the questions after the scan, because no crawler can see whether you have a staging environment.

Three outcomes, not two

A check passes, fails, or reports that we could not determine it. The third is the one that matters: a check we could not settle is never counted as a pass, and never moves your score in either direction. A scanner that quietly turns "we did not look" into a green tick is the one to distrust.

How it reaches the score

Every site starts at 100 and each failed check deducts a fixed amount by severity, with a cap per category so one weak area cannot sink the whole number. The arithmetic is public. See exactly how the score is produced.

Exposure 9

Revoke the exposed Slack token

A Slack token can read and post messages in every channel it can reach, letting an attacker read private conversations or impersonate a workspace member.

Rotate the exposed SendGrid API key

A SendGrid API key can send email as the domain's verified sender, which is enough to run a convincing phishing campaign that looks like it came from a trusted source.

Rotate the exposed Supabase service-role key

A Supabase service_role key bypasses row-level security entirely, granting full read and write access to every table in the database rather than the narrow slice an authenticated user is meant to see.

Restrict or rotate the exposed Google API key

An unrestricted Google API key lets anyone who finds it make billable calls against the associated project, running up usage charges or exhausting quota that other features depend on.

Stop shipping source maps publicly

A published source map republishes the original, unminified source (including comments and internal structure never meant to ship), making it far easier to find other vulnerabilities in the same codebase.

Revoke the exposed private key

A private key is the credential a certificate, SSH host, or signing system trusts completely: holding it lets an attacker impersonate that system, decrypt its traffic, or sign as it.

Revoke the exposed GitHub token

A GitHub token inherits its scope's permissions, which can include reading private source code, pushing commits, or reading other secrets stored in the repository or its CI pipeline.

Rotate the exposed Stripe secret key

A live Stripe secret key can issue refunds, create charges, and read customers' payment and order history: whoever holds it can act as the business, not just view its data.

Rotate the exposed AWS access key

An AWS access key inherits whatever permissions are attached to it, which can mean reading files from storage buckets, spinning up billable compute, or altering infrastructure outright.

Security 18

Serve the site over HTTPS

Without HTTPS, browsers flag the site as "Not Secure" and all traffic, including passwords and payment details, travels in plaintext, exposing customers to interception and eroding trust.

Enable HSTS

Without HSTS, a user's first visit or any link they click can be silently downgraded to plain HTTP by an attacker on the network, exposing them to session hijacking even though HTTPS is available.

Fix the Strict-Transport-Security max-age

A Strict-Transport-Security header only protects repeat visitors for as long as its max-age lasts, and a header with no valid max-age at all is ignored outright by browsers. Either state (expired, deliberately zeroed out mid-rollback, or malformed) leaves every visitor as exposed to a downgrade attack on their next visit as if the header had never been sent, even though it's present in every response.

Add a Content-Security-Policy header

Without a Content-Security-Policy, the browser will run any script that ends up on the page, including ones an attacker injects, which is the primary way customer data gets stolen and sessions get hijacked.

Enforce the existing Content-Security-Policy

A report-only policy only logs violations; it does not stop a malicious script from running. The hard part (defining the policy and watching it against real traffic to catch breakage) is already done, so the remaining risk is closed by a one-line header rename, not a redesign.

Add X-Content-Type-Options header

Without nosniff, browsers may reinterpret a file as a different type, for example treating an uploaded image as executable JavaScript, which attackers can exploit to run malicious scripts.

Add clickjacking protection

Without clickjacking protection, an attacker can embed the site in an invisible iframe and trick users into clicking real buttons (like "confirm payment") while they believe they are interacting with another page.

Add a Referrer-Policy header

Without a Referrer-Policy, full page URLs (which may contain session tokens, search terms, or internal paths) are leaked to every third-party site a user navigates to or loads a resource from.

Tighten the Referrer-Policy value

unsafe-url sends the full page URL (including query strings, which can carry session tokens, search terms, or internal paths) to every destination a user navigates to or loads a resource from, even when that destination is reached over a downgraded, insecure connection. It is a one-line value change, not a missing header.

Add a Permissions-Policy header

Without a Permissions-Policy, any script running on the page, including third-party scripts and ads, can request access to the camera, microphone, or location, widening the damage a compromised script can do.

Migrate Feature-Policy to Permissions-Policy

Feature-Policy was renamed and restructured as Permissions-Policy years ago, and current Chromium-based browsers no longer read the old header at all: a site that only ships Feature-Policy is protected in no browser that matters today, even though the restriction was correctly designed once. The syntax is a near-literal translation, so the fix is a rename with a small syntax change, not a redesign.

Mark cookies as Secure

Cookies without the Secure attribute can still be sent over an unencrypted connection if the browser is ever made to request the site over plain HTTP, exposing whatever value the cookie holds to anyone on the same network.

Ask the tracking vendor to mark their cookies Secure

These cookies are written by third-party tracking scripts running on the site (Google Analytics, Google Ads, Microsoft Clarity, Meta Pixel, Microsoft Advertising, and similar), not by the site's own server. "Add Secure to the cookies the app sets" does not apply, because the app never sets them; the vendor's script does, and the vendor's own configuration controls the attributes it uses.

Mark cookies as HttpOnly

Cookies without HttpOnly are readable by any JavaScript on the page, so a single XSS bug is enough for an attacker to steal session cookies and take over accounts.

Stop exposing server software versions

Advertising exact server or framework versions gives attackers a shortcut to find and exploit known vulnerabilities for that specific version, without any extra reconnaissance.

Fix mixed content

Mixed content (HTTP resources loaded on an HTTPS page) is blocked or flagged by modern browsers and gives attackers a way to tamper with an otherwise-secure page by intercepting the insecure resource.

Set up dependency vulnerability scanning

Most breaches exploit known vulnerabilities in outdated dependencies; without automated scanning these sit unnoticed until someone exploits them.

Keep up security hardening

Security posture erodes silently as new code, third-party scripts, or config changes land, so a clean check today does not guarantee a clean check next quarter.

Performance 11

Reduce third-party request volume

On sites already served over HTTP/2 or HTTP/3, first-party bundling stops being the dominant performance cost: third-party tags (analytics, ads, chat widgets, fonts) usually are, and unlike first-party code the owner cannot bundle them at build time, only choose to run fewer of them.

Optimize large images

Oversized images are one of the single biggest, easiest-to-fix causes of slow pages, and slow pages lose visitors before the content even renders.

Resize the oversized images already in a modern format

An oversized image is still wasted bytes on every load even when its format is already efficient: a modern format compresses better per pixel, but delivering an image far larger than its display size throws that saving away.

Speed up page load time

Slow page loads are one of the strongest predictors of visitors leaving before the page even finishes loading, directly costing traffic and conversions.

Enable text compression

Serving HTML/JS/CSS uncompressed can multiply transfer size several times over, directly slowing every page load for every visitor.

Add cache-control headers to static assets

Without cache headers, returning visitors re-download the same images, scripts, and stylesheets on every visit, wasting bandwidth and slowing repeat page loads unnecessarily.

Reduce render-blocking resources

Render-blocking CSS and JS delay the moment visitors see anything on screen, making the site feel slow even when total page weight is reasonable.

Keep an eye on performance

Performance regressions tend to creep in one image or script at a time, so a fast site today can quietly slow down without ongoing checks.

Reduce page weight

Heavier pages take longer to load, especially on mobile networks, and slow load times directly correlate with higher bounce rates and lower conversion.

Reduce the number of requests

Every extra request adds latency, especially on slower connections; an excessive request count is a common, fixable reason pages feel sluggish even when total size looks fine.

Add real-user performance monitoring

Lab scores miss what real users on real devices and networks experience; field data is what Google ranks on and what actually affects conversions.

SEO 12

Add a page title

Without a page title, search engines have no reliable label to show in results and browser tabs are blank, hurting both click-through rate and basic usability.

Fix page title length

Titles that are too short waste an opportunity to describe the page, and titles that are too long get truncated with '...' in search results, hiding the message you're trying to send.

Add a meta description

Without a meta description, search engines auto-generate a snippet from the page's content (often an awkward excerpt), reducing the chance a searcher clicks through.

Fix meta description length

A meta description that's too short under-sells the page, and one that's too long gets cut off in search results. Both reduce click-through rate.

Use exactly one H1 per page

Multiple or missing H1s confuse search engines and screen readers about what the page's main topic actually is, diluting SEO relevance and hurting accessibility.

Add a viewport meta tag

Without a viewport tag, mobile browsers render the page at desktop width and shrink it, making text unreadable and hurting both mobile usability and Google's mobile-first ranking.

Add a canonical link

Without a canonical tag, search engines can split ranking signals across near-duplicate URLs (with tracking params, trailing slashes, etc.), diluting the page's authority.

Allow the page to be indexed

A noindex directive tells search engines to leave the page out of results entirely, and a robots.txt Disallow stops them fetching it in the first place. If either isn't intentional, the page and any traffic or leads it could generate are invisible to search.

Add Open Graph tags

Without Open Graph tags, links shared on social platforms and chat apps show a generic or broken preview, which measurably reduces click-through on shared links.

Add a favicon

A missing favicon makes the site look unfinished or untrustworthy in browser tabs, bookmarks, and history (often one of the first things a visitor notices).

Add a language attribute

Without a declared language, search engines may serve the page to the wrong locale's audience and screen readers may mispronounce content, hurting both reach and accessibility.

Connect Google Search Console

Without Search Console you are blind to indexing errors, manual actions, and the queries that bring you traffic.

Accessibility 4

Runtime Errors 3

Best Practices 8

Run your tests automatically on every change (CI)

Without CI, regressions reach production because nothing verifies changes automatically; CI turns "hope it works" into "proven before merge".

Add an HTML5 doctype

Without a doctype, browsers render the page in "quirks mode", which can cause inconsistent layout and CSS behavior across browsers.

Declare a character encoding

Without a declared charset, browsers may guess the encoding incorrectly, causing special characters, accents, or symbols to render as garbled text.

Redirect HTTP to HTTPS

If HTTP requests aren't redirected, visitors and search engines who land on the insecure URL stay on it, exposing them to interception and splitting SEO signal between the HTTP and HTTPS versions.

Add a responsive viewport tag

Without a responsive viewport tag, the page renders at desktop width on phones, forcing visitors to pinch and zoom, which drives them away.

Set up a staging environment

Without a staging environment, every change is tested for the first time in production, turning routine deploys into high-risk events that can take the live site down.

Add uptime monitoring & alerting

Without uptime monitoring, outages and errors are usually discovered by customers complaining rather than by the team, which extends downtime and erodes trust.

Establish a regular release cadence

Shipping less than once a month means fixes and improvements pile up, changes get riskier and harder to review, and the team loses the ability to respond quickly to bugs or opportunities.

Ready to see where you stand?

Scan your site and get your Engineering Score with a prioritized roadmap in under a minute, no signup required.

Scan your site