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.