From isagentready
Fixes security and trust issues — configures HTTPS, HSTS, Content-Security-Policy, X-Content-Type-Options, frame protection, CORS, and Referrer-Policy headers so AI agents and platforms trust the website for interaction. Use when asked to "fix security headers", "add HSTS", "configure CSP", "fix HTTPS", "improve security score", "add security headers", "fix CORS", "add Referrer-Policy", or any security header configuration task.
How this skill is triggered — by the user, by Claude, or both
Slash command
/isagentready:security-trustThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Fixes Category 5 (Security & Trust, 15% weight) issues from [IsAgentReady.com](https://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.
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.
ai-content-discovery skill)structured-data skill)content-semantics skill)agent-protocols skill)| 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 |
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)'
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.
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
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.
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
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.
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-Only first:
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
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.
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
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.
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
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.
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
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.
Check current state:
curl -sI https://example.com/ | grep -i referrer-policy
Add the header — strict-origin-when-cross-origin is 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
Common mistakes that cause checkpoint failures:
max-age=86400 (1 day) scores only 10/20; use max-age=31536000 (1 year)upgrade-insecure-requests alone scores only 5/15frame-ancestors insteadAccess-Control-Allow-Origin: * on endpoints that use cookies or tokensSee references/gotchas.md for detailed correct vs incorrect examples of each.
If $ARGUMENTS is provided, interpret it as the URL to fix or the specific checkpoint to address.
npx claudepluginhub bartwaardenburg/isagentready-skills --plugin isagentreadyProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.