security-trust
Security & Trust
Fixes Category 5 (Security & Trust, 15% weight) issues from IsAgentReady.com. This category checks whether AI agents and platforms can trust your website for secure interaction. It evaluates 7 checkpoints worth 75 points total.
When to Use
- Fixing HTTPS or SSL certificate issues
- Adding or configuring HSTS (Strict-Transport-Security)
- Creating or improving Content-Security-Policy
- Adding X-Content-Type-Options, X-Frame-Options, or frame-ancestors
- Fixing CORS configuration (Access-Control-Allow-Origin)
- Adding or fixing Referrer-Policy headers
- Any task to "improve security score" or "add security headers"
When NOT to Use
- Fixing robots.txt, sitemaps, or AI crawler access (use
ai-content-discoveryskill) - Adding structured data / JSON-LD (use
structured-dataskill) - Fixing semantic HTML or heading hierarchy (use
content-semanticsskill) - Setting up agent protocols like WebMCP or A2A (use
agent-protocolsskill)
Checkpoints Overview
| ID | Checkpoint | Max Points | What It Tests |
|---|---|---|---|
| 5.1 | HTTPS | 10 | Final URL uses https://, valid SSL certificate |
| 5.3 | HSTS header | 20 | Strict-Transport-Security with max-age and includeSubDomains |
| 5.4 | Content-Security-Policy | 15 | CSP header with meaningful directives |
| 5.5 | X-Content-Type-Options | 5 | Must be exactly "nosniff" |
| 5.6 | Frame protection | 5 | X-Frame-Options or CSP frame-ancestors |
| 5.7 | CORS configuration | 10 | Access-Control-Allow-Origin (absence or specific origin) |
| 5.9 | Referrer-Policy | 10 | Header present with a safe value |
Fix All Headers at Once
The fastest path to a perfect security score is adding all 7 headers in a single configuration change.
See references/server-configs.md for complete copy-paste configs for Nginx, Apache, Caddy, Vercel, Netlify, Cloudflare Workers, Express.js, Next.js, and Django.
After applying your config, verify all headers at once:
curl -sI https://example.com/ | grep -iE '(strict-transport|content-security|x-content-type|x-frame|access-control|referrer-policy)'
Checkpoint 5.1: HTTPS (10 pts)
What passes: Final URL uses https:// with a valid SSL certificate (10 pts).
Partial credit: HTTPS with an invalid/expired certificate (4 pts).
What fails: HTTP without redirect to HTTPS (0 pts).
Why it matters: HTTPS is the baseline for secure communication. AI agents and crawlers refuse to interact with insecure sites — no agent framework will send credentials or user data over plain HTTP.
Fix Workflow
-
Check current state:
curl -sI http://example.com/ | head -10 curl -sI https://example.com/ | head -10 -
Install a free SSL certificate with Certbot (Let's Encrypt):
# Ubuntu/Debian with Nginx sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d example.com -d www.example.com # Ubuntu/Debian with Apache sudo apt install certbot python3-certbot-apache sudo certbot --apache -d example.com -d www.example.com -
Force HTTPS redirect:
Nginx:
server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }Apache (
.htaccess):RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] -
Set up auto-renewal:
sudo certbot renew --dry-run # Certbot adds a cron/systemd timer automatically -
Verify:
curl -sI https://example.com/ | head -5 # Should show HTTP/2 200 or HTTP/1.1 200
References: Let's Encrypt, web.dev: Why HTTPS Matters
Checkpoint 5.3: HSTS Header (20 pts)
What passes: Strict-Transport-Security header with max-age>=31536000 and includeSubDomains (20 pts).
Partial credit: max-age>=31536000 without includeSubDomains (16 pts), or short max-age (10 pts).
What fails: Header missing or max-age=0 (0 pts).
Why it matters: HSTS prevents protocol downgrade attacks and SSL stripping. It tells browsers to always use HTTPS, even if a user types http://. Without HSTS, a man-in-the-middle can intercept the initial HTTP request before the redirect.
Fix Workflow
-
Check current state:
curl -sI https://example.com/ | grep -i strict-transport -
Add the HSTS header:
Nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;Apache:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"Caddy (automatic — HSTS is enabled by default).
-
Verify:
curl -sI https://example.com/ | grep -i strict-transport # Expected: strict-transport-security: max-age=31536000; includeSubDomains; preload -
Optional: submit to HSTS preload list at hstspreload.org — this hardcodes HSTS into browsers so even the first visit is forced HTTPS.
See references/security-headers.md for max-age math and the preload submission process. See references/gotchas.md for the "max-age too short" pitfall.
References: MDN: Strict-Transport-Security, HSTS Preload List
Checkpoint 5.4: Content-Security-Policy (15 pts)
What passes: CSP header with meaningful directives like default-src, script-src, style-src (15 pts).
Partial credit: CSP header with only trivial directives (upgrade-insecure-requests or block-all-mixed-content) (5 pts).
What fails: No CSP header (0 pts).
Why it matters: CSP prevents XSS and code injection attacks. For AI agents, CSP ensures the content they read hasn't been tampered with by injected scripts — it's a signal that the site maintains content integrity.
Fix Workflow
-
Check current state:
curl -sI https://example.com/ | grep -i content-security-policy -
Add a starter CSP — restrictive but practical:
Nginx:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'" always;Apache:
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'" -
If you use third-party scripts (analytics, CDNs), whitelist them:
script-src 'self' https://cdn.example.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.example.com; -
Test before enforcing — use
Content-Security-Policy-Report-Onlyfirst:add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; report-uri /csp-report" always; -
Verify:
curl -sI https://example.com/ | grep -i content-security-policy # Should show meaningful directives, not just upgrade-insecure-requests
See references/security-headers.md for the full directive reference and nonce-based CSP. See references/gotchas.md for the "trivial CSP" pitfall.
References: MDN: Content-Security-Policy, OWASP: CSP Cheat Sheet
Checkpoint 5.5: X-Content-Type-Options (5 pts)
What passes: Header value is exactly nosniff (5 pts).
What fails: Header missing or any other value (0 pts).
Why it matters: Prevents MIME type sniffing attacks where browsers interpret files as a different content type than declared, potentially executing malicious code disguised as harmless files.
Fix Workflow
-
Check current state:
curl -sI https://example.com/ | grep -i x-content-type -
Add the header:
Nginx:
add_header X-Content-Type-Options "nosniff" always;Apache:
Header always set X-Content-Type-Options "nosniff" -
Verify:
curl -sI https://example.com/ | grep -i x-content-type # Expected: x-content-type-options: nosniff
References: MDN: X-Content-Type-Options
Checkpoint 5.6: Frame Protection (5 pts)
What passes: X-Frame-Options: DENY or SAMEORIGIN, OR CSP frame-ancestors directive (5 pts).
What fails: Neither header present (0 pts).
Why it matters: Prevents clickjacking attacks where your site is embedded in a malicious iframe. This protects users who follow AI-suggested links from being tricked into interacting with hidden content.
Fix Workflow
-
Check current state:
curl -sI https://example.com/ | grep -iE '(x-frame-options|frame-ancestors)' -
Add frame protection — choose one approach:
Option A: X-Frame-Options (widely supported):
# Nginx add_header X-Frame-Options "DENY" always;# Apache Header always set X-Frame-Options "DENY"Option B: CSP frame-ancestors (modern, more flexible):
# Add to your existing CSP: add_header Content-Security-Policy "frame-ancestors 'none'; ..." always;Option C: Both (belt and suspenders):
add_header X-Frame-Options "DENY" always; add_header Content-Security-Policy "frame-ancestors 'none'; default-src 'self'" always; -
If your site needs to be embedded by specific origins:
add_header X-Frame-Options "SAMEORIGIN" always; # Or with CSP (more precise): add_header Content-Security-Policy "frame-ancestors 'self' https://trusted.example.com" always; -
Verify:
curl -sI https://example.com/ | grep -iE '(x-frame|frame-ancestors)'
See references/gotchas.md for the deprecated
ALLOW-FROMpitfall.
References: MDN: X-Frame-Options, MDN: CSP frame-ancestors
Checkpoint 5.7: CORS Configuration (10 pts)
What passes: No Access-Control-Allow-Origin header (secure default, 10 pts), or specific origin (10 pts).
Partial credit: Wildcard * (7 pts).
What fails: N/A — any response scores at least 7 if the header is present.
Why it matters: CORS controls which origins can make cross-origin requests to your site. Proper configuration protects your APIs while allowing legitimate agent integrations. A wildcard * isn't dangerous for public content but signals less intentional security posture.
Fix Workflow
-
Check current state:
curl -sI https://example.com/ | grep -i access-control-allow-origin -
If no header is present — you already score 10/10. No action needed.
-
If you see
Access-Control-Allow-Origin: *and want full points — restrict to specific origins:Nginx:
# Instead of: add_header Access-Control-Allow-Origin "*"; # Use a map for dynamic origin matching: map $http_origin $cors_origin { default ""; "https://app.example.com" $http_origin; "https://agent.example.com" $http_origin; } add_header Access-Control-Allow-Origin $cors_origin always;Express.js:
const cors = require('cors'); app.use(cors({ origin: ['https://app.example.com', 'https://agent.example.com'] })); -
If you need wildcard for a public API — that's fine, you still get 7/10.
-
Verify:
curl -sI -H "Origin: https://app.example.com" https://example.com/api/ | grep -i access-control
See references/gotchas.md for the "wildcard on authenticated endpoints" pitfall.
References: MDN: CORS
Checkpoint 5.9: Referrer-Policy (10 pts)
What passes: Referrer-Policy header present with a safe value (10 pts).
What fails: Header missing (0 pts) or value is unsafe-url (0 pts).
Safe values: no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin.
Why it matters: Controls what referrer information leaks to third parties. Without it, full URLs (including query parameters with tokens, session IDs) may be sent to external sites.
Fix Workflow
-
Check current state:
curl -sI https://example.com/ | grep -i referrer-policy -
Add the header —
strict-origin-when-cross-originis the recommended default:Nginx:
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Apache:
Header always set Referrer-Policy "strict-origin-when-cross-origin" -
Verify:
curl -sI https://example.com/ | grep -i referrer-policy # Expected: referrer-policy: strict-origin-when-cross-origin
See references/gotchas.md for the "unsafe-url" pitfall.
References: MDN: Referrer-Policy
Key Gotchas
Common mistakes that cause checkpoint failures:
- HSTS max-age too short —
max-age=86400(1 day) scores only 10/20; usemax-age=31536000(1 year) - CSP with only trivial directives —
upgrade-insecure-requestsalone scores only 5/15 - HSTS without includeSubDomains — scores 16/20 instead of 20/20
- X-Frame-Options ALLOW-FROM — deprecated, not supported by modern browsers; use CSP
frame-ancestorsinstead - CORS wildcard on auth endpoints —
Access-Control-Allow-Origin: *on endpoints that use cookies or tokens - unsafe-url Referrer-Policy — leaks full URLs including query parameters; scores 0/10
- Nginx add_header without "always" — headers only sent on 2xx responses, missing on 3xx/4xx/5xx
See references/gotchas.md for detailed correct vs incorrect examples of each.
References
- security-headers.md — Deep dive on all 7 headers: purpose, valid values, scoring, impact
- server-configs.md — Copy-paste configs for Nginx, Apache, Caddy, Vercel, Netlify, Cloudflare, Express, Next.js, Django
- gotchas.md — Common pitfalls with wrong vs correct examples
Instructions
- Identify failing checkpoints from the IsAgentReady.com scan results
- For the fastest fix, use the "Fix All Headers at Once" section with a server config from references/server-configs.md
- For individual fixes, follow the fix workflow for each failing checkpoint
- Verify all headers with the curl command in the "Fix All Headers at Once" section
- Re-scan at isagentready.com to confirm improvements
If $ARGUMENTS is provided, interpret it as the URL to fix or the specific checkpoint to address.
More from bartwaardenburg/isagentready-skills
content-semantics
Fixes content and semantic HTML issues — implements server-side rendering, heading hierarchy, semantic HTML elements, ARIA landmarks, image alt text, language attributes, descriptive link texts, question-based headings, form labels, autocomplete hints, keyboard navigability, document titles, button accessible names, and aria-hidden misuse so AI agents can navigate and understand page content via the accessibility tree. Use when asked to "fix semantic HTML", "add SSR", "fix heading hierarchy", "add alt text", "improve semantics score", "fix accessibility for AI", "add ARIA landmarks", "fix language attribute", "add question headings", "fix form labels", "add autocomplete", "fix keyboard navigation", "fix document title", "fix button names", "fix aria-hidden", or any semantic HTML task.
12agent-protocols
Implements AI agent interaction protocols — adds WebMCP declarative tool annotations, WebMCP manifests, Google A2A Agent Cards, MCP discovery documents, OpenAPI specifications, and agents.json so AI agents can directly interact with website functionality. Use when asked to "add WebMCP", "implement A2A", "create agent card", "add MCP discovery", "create OpenAPI spec", "add agents.json", "improve agent protocols score", "make site agent-interactive", or any WebMCP, A2A, MCP, or OpenAPI task.
11structured-data
Fixes structured data issues — adds and validates JSON-LD markup with Schema.org types including Organization, WebSite, Product, Article, FAQPage, BreadcrumbList, entity linking, and author attribution so AI search engines can understand and cite website content. Use when asked to "add JSON-LD", "fix structured data", "add schema markup", "improve structured data score", "add Organization schema", "add BreadcrumbList", "fix Schema.org", "add product markup", "add FAQ schema", "add author markup", or any JSON-LD or Schema.org task.
11ai-content-discovery
Fixes AI content discovery issues — creates and optimizes robots.txt, AI crawler directives, XML sitemaps, llms.txt, meta robots tags, and content freshness signals so AI systems can find, crawl, and understand website content. Use when asked to "fix robots.txt", "add llms.txt", "create a sitemap", "allow AI crawlers", "fix AI discoverability", "improve AI content discovery score", "make site crawlable by AI", "add dateModified", "fix content freshness", or any robots.txt, sitemap, or llms.txt task.
11