Website report card
Grade a website's headers, email records and search tags in one report
Paste the output of curl -sI (plus, if you like, the page's <head>, robots.txt and
SPF/DMARC records) and get a grade per area with the nginx, Apache, Cloudflare, DNS or HTML line that
fixes each problem. Nothing leaves this tab.
Report
| Area | Grade | Score | Issues |
|---|---|---|---|
| Security headers | C | 6 medium | |
| Content-Security-Policy | C | 1 high2 medium | |
| Email authentication | B | 2 medium | |
| Crawlability | A | none | |
| Meta and social tags | C | 1 high1 medium |
Security headers
C64 / 100 · weight 30HTTP/2 200 · 10 headers · open the full security headers tool
- Medium
HSTS max-age is 183 days
Reasonable, but below the one year (31536000) the preload list requires.
Fix Send HSTS for one year on HTTPS responses. Add includeSubDomains only once every subdomain serves HTTPS.
Show nginx · Apache · Cloudflare
nginx add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;Apache Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"Cloudflare SSL/TLS > Edge Certificates > HTTP Strict Transport Security (HSTS) Enable, Max Age 12 months, include subdomains if every subdomain is HTTPS - Medium
Referrer-Policy is not set
Current Chrome, Firefox and Safari default to strict-origin-when-cross-origin, which is sensible. Setting it explicitly makes the behaviour the same in every browser.
Fix Send only the origin to other sites, never the full URL.
Show nginx · Apache · Cloudflare
nginx add_header Referrer-Policy "strict-origin-when-cross-origin" always;Apache Header always set Referrer-Policy "strict-origin-when-cross-origin"Cloudflare Rules > Transform Rules > Modify Response Header Set static Referrer-Policy Value strict-origin-when-cross-origin - Medium
Permissions-Policy is missing
Optional, but a short policy such as camera=(), microphone=(), geolocation=() stops embedded third-party content from asking for those permissions.
Fix Switch off browser features the site does not use. List any feature you do use as, for example, geolocation=(self).
Show nginx · Apache · Cloudflare
nginx add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;Apache Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()"Cloudflare Rules > Transform Rules > Modify Response Header Set static Permissions-Policy Value camera=(), microphone=(), geolocation=(), payment=() - Medium
Server header reveals a version: nginx/1.24.0
A version number tells an attacker which known vulnerabilities to try. Most servers can be told to omit it (nginx: server_tokens off).
Fix Hide the version number.
Show nginx · Apache · Cloudflare
nginx server_tokens off; # in the http blockApache ServerTokens Prod ServerSignature OffCloudflare Rules > Transform Rules > Modify Response Header Remove Server - Medium
x-powered-by: PHP/8.2.12
Advertises the software stack for no benefit to visitors. Remove it.
Fix Stop advertising the application stack. For PHP, expose_php = Off in php.ini removes it at the source.
Show nginx · Apache · Cloudflare
nginx fastcgi_hide_header X-Powered-By; # PHP-FPM proxy_hide_header X-Powered-By; # reverse proxyApache Header always unset X-Powered-ByCloudflare Rules > Transform Rules > Modify Response Header Remove X-Powered-By - Medium
Cookie session lacks Secure, SameSite
Secure keeps it off plain HTTP; HttpOnly hides it from JavaScript (omit only if scripts must read it); SameSite limits cross-site sending (Chromium defaults to Lax when absent).
Fix Set the missing attributes where the cookie is created. The server-level lines add them to every cookie the backend sets.
Show App · nginx · Apache
App Set-Cookie: session=...; Path=/; Secure; HttpOnly; SameSite=Laxnginx proxy_cookie_flags ~ secure httponly samesite=lax; # nginx 1.19.3+Apache Header always edit Set-Cookie ^(.*)$ "$1; Secure; HttpOnly; SameSite=Lax"
2 checks passed
- Pass
X-Frame-Options: SAMEORIGIN
Other sites cannot put this page in a frame, which prevents clickjacking.
- Pass
X-Content-Type-Options: nosniff
Browsers will not guess a different content type than the one declared, which stops some upload-based attacks.
Content-Security-Policy
C66 / 100 · weight 20Content-Security-Policy header · 3 directives · open the full content-security-policy tool
- High
script-src: 'unsafe-inline' allows inline script
Any injected <script> block or onclick= attribute runs, which is exactly what CSP exists to prevent. Replace it with nonces or hashes.
Fix Give each inline script a per-response nonce and drop 'unsafe-inline'. The application has to generate the nonce; a static web server cannot.
Show CSP · HTML
CSP script-src 'nonce-{random per response}' 'strict-dynamic';HTML <script nonce="{same random value}">...</script> - Medium
object-src is 'self' (from default-src)
Recommended value is 'none'. It costs nothing on a modern site.
Fix Add object-src 'none'. Nothing on a current site needs <object> or <embed>.
Show CSP
CSP object-src 'none'; - Medium
No base-uri
base-uri does not fall back to default-src. Without it, an injected <base href> tag can redirect every relative script URL on the page to an attacker's host, bypassing nonce-based policies. Set base-uri 'none' or 'self'.
Fix Add base-uri 'self' (or 'none' if the site has no <base> tag).
Show CSP
CSP base-uri 'self'; - Note
script-src: host allowlist (1 host)
Allowlists are often bypassable: if any listed host serves JSONP endpoints, user uploads or an old copy of AngularJS, an attacker can run script from it. Nonces with 'strict-dynamic' avoid the problem.
- Note
No frame-ancestors
Other sites may embed this page in a frame (clickjacking risk) unless X-Frame-Options is set. frame-ancestors 'self' or 'none' is the modern replacement for X-Frame-Options.
Fix Add frame-ancestors 'self' to control who can frame the page.
Show CSP
CSP frame-ancestors 'self'; - Note
No form-action
form-action does not fall back to default-src, so an injected form could post to any host. form-action 'self' is a cheap addition if all forms submit to your own site.
Fix Add form-action 'self' if every form on the site submits to the site itself.
Show CSP
CSP form-action 'self'; - Note
No reporting endpoint
Without report-uri or report-to you will not hear about violations, including ones that break your own pages.
Fix Add a report-uri (or report-to) endpoint to hear about violations. Any URL that accepts a POST will do; report collection services give you one.
Show CSP
CSP report-uri /csp-report;
Email authentication
B82 / 100 · weight 203 TXT records: SPF, DMARC, DKIM · open the full email authentication tool
- Medium
DMARC: p=none: monitoring only
Mail that fails DMARC is still delivered normally. That is the right first step while you read reports; move to quarantine, then reject, once legitimate senders all pass. Google and Yahoo require at least p=none from bulk senders.
Fix Once the reports show every legitimate sender passing, move to quarantine, then reject.
Show DNS
DNS _dmarc.example.com. TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@example.com" - Medium
DKIM: RSA key of 1024 bits
1024-bit keys still verify, but RFC 8301 recommends 2048 bits for signers, and 1024-bit RSA is within reach of well-funded attackers. Rotate to 2048.
Fix Generate a 2048-bit key under a new selector in your mail provider's settings, publish it, switch signing to it, then retire the old selector.
- Note
SPF: 2 DNS-querying terms at the top level
Each include: pulls in another record whose own lookups count toward the same limit of 10, and this tool cannot see inside them. Check the total with the record of each included domain.
2 checks passed
- Pass
SPF: ~all (softfail)
Unlisted senders softfail: most receivers accept but mark or score the message. With DMARC in place, many operators keep ~all deliberately and let DMARC decide.
- Pass
DMARC: Aggregate reports go to 1 address
If a report address is on a different domain, that domain must publish an authorisation record (RFC 7489 7.1), which report services set up for you.
Crawlability
A100 / 100 · weight 15robots.txt · 3 groups · open the full crawlability tool
- Note
Blocks GPTBot
These are AI crawlers, not search engines, so blocking them does not affect search ranking. It is a choice, not an error.
- Note
Line 15: crawl-delay: 10
Not part of RFC 9309. Bing and Yandex honour it; Google ignores it.
2 checks passed
- Pass
Googlebot and Bingbot may crawl the homepage
Tested against the path / with the RFC 9309 matching rules.
- Pass
Sitemap listed (1)
https://www.example.com/sitemap.xml
Meta and social tags
C67 / 100 · weight 1510 meta tags, 1 link tags · open the full meta and social tags tool
- High
og:image is not an absolute URL
"/images/share-card.jpg" — crawlers fetch the image directly and do not resolve relative paths. Use the full https:// URL.
Fix Use the image's full https:// URL.
Show HTML
HTML <meta property="og:image" content="https://www.example.com/share-card.png"> - Medium
Title is 86 characters
Google cuts titles by pixel width (about 600px), which is typically 50–60 characters; the rest becomes "…". Put the words that matter first.
Fix Shorten the title to about 60 characters and put the important words first.
- Note
No og:image:width / og:image:height
Optional, but declaring them lets some platforms render the card on the first share instead of after fetching the image.
- Note
No og:image:alt
Alt text for the preview image, read by screen readers on platforms that support it.
4 checks passed
- Pass
Description is 109 characters
- Pass
Canonical URL set
https://www.example.com/coffee/
- Pass
Required Open Graph tags present
og:title, og:type, og:image and og:url.
- Pass
twitter:card is summary_large_image
Single checks
Each area of the report in full detail, plus three more decoders.
- HTTP security headers checkerRFC 6797 · Fetch Paste the response headers from curl or your browser and get a grade, plus a line-by-line verdict on HSTS, CSP, framing, nosniff, Referrer-Policy and Permissions-Policy.
- Content-Security-Policy evaluatorW3C CSP Level 3 Parse a CSP directive by directive, see what each resource type falls back to, and find unsafe-inline, wildcards, missing object-src and base-uri.
- SSL/TLS certificate decoderRFC 5280 Decode a PEM certificate: subject, issuer, every SAN, validity dates, key type and size, extensions and SHA-256 fingerprint, with a hostname check.
- SPF, DMARC and DKIM record checkerRFC 7208 · 7489 · 6376 Paste the TXT records for a domain and get each one parsed and checked against its RFC: SPF lookups and all, DMARC policy and reporting, DKIM key size.
- Meta tag and Open Graph checkerogp.me Paste a page’s HTML head to check the title, description, canonical, Open Graph and X card tags, with approximate search and share previews.
- robots.txt testerRFC 9309 Paste a robots.txt, pick a crawler and a URL, and see whether it is allowed and which line decides it, using the RFC 9309 matching rules.
- JWT decoderRFC 7519 Decode a JSON Web Token’s header and claims, turn timestamps into dates, and flag alg=none, missing expiry and secrets in the payload. Decode only; nothing is sent.
- User-agent string parserRFC 9110 Break a User-Agent header into browser, engine, operating system, device and crawler, and see which parts modern browsers now freeze or fake.
Where to get what you paste
macOS and Linux terminals; on Windows use curl.exe and nslookup -type=TXT.
| To check | Run or do | Then paste into |
|---|---|---|
| Response headers | curl -sIL https://example.com/ | Headers |
| Content-Security-Policy | curl -sI https://example.com/ | grep -i content-security | CSP |
| TLS certificate chain | openssl s_client -connect example.com:443 -servername example.com -showcerts | Certificate |
| SPF, DMARC, DKIM records | dig +short TXT _dmarc.example.com | SPF/DMARC |
| Meta and Open Graph tags | View source (Ctrl+U), copy the <head> | Meta tags |
| robots.txt | curl -s https://example.com/robots.txt | robots.txt |
Who this is for
People who look after websites without doing it all day: the developer who also runs the server, the agency setting up a client's email, the site owner whose link previews look wrong, the person asked to "sort out the security headers" after an audit. The tools answer the question in front of you and tell you which standard the answer comes from, so you can check it and explain it to someone else.
Why you paste instead of typing a URL
Most "check my site" tools send your URL to their server, which fetches the site and reports back. WebPeeks has no server doing that, and a browser does not let one site read another site's headers or certificate (the CORS rules). So you fetch the data with one command and paste the result.
That has two useful side effects. Nothing you check passes through anyone else's server, which matters when it is an internal hostname, a staging site or a token. And you can check things that are not public yet: a robots.txt you are still drafting, a DNS record before you publish it, or the headers from a server on your own network.
How the checks are made
Each tool follows the published standard for what it reads: RFC 5280 for certificates, RFC 9309 for robots.txt, RFC 7208, 6376 and 7489 for SPF, DKIM and DMARC, the W3C CSP Level 3 specification, RFC 7519 for JWTs. Where there is no standard, as with the grades, the page says the weighting is this site's own and shows it in full. The about page lists every method and its limits in one place.