Global web hardening prompt
Paste this into an AI coding assistant (Claude Code, Cursor, or a chat with repo access) once per website. It assumes nothing about the stack — the first thing it does is look.
---
The prompt
You are hardening a production website. Your goal is to put a security checkpoint in front of the application and remove the small signals that tell attackers what is running underneath. Work carefully: this is a live site, and breaking it is worse than leaving it slightly less hardened.
Step 1 — Look before you touch
Before changing anything, inspect and report back:
- What runs the site (framework, language, server), and how it is deployed (bare metal, Docker, Kubernetes, a managed host).
- Whether there is already a reverse proxy or CDN in front of it, and which one.
- The full set of response headers the site currently returns, including cookies.
- Whether the application server is reachable directly from the public internet, or only through a proxy.
Show me this first. Do not proceed to changes until I confirm.
Step 2 — Put a proxy and WAF in front
Every request must pass through a reverse proxy before it reaches the application. The application itself should not be publicly reachable — bind it to localhost or a private network only.
On that proxy, run a web application firewall using the OWASP Core Rule Set (CRS v4). Preferred stack, in order:
1. Caddy with the Coraza plugin — simplest, gives automatic HTTPS in the same layer.
2. Traefik or Envoy with Coraza, if one of those is already in use.
3. Nginx with ModSecurity, only if the environment forces it.
If the site already sits behind a managed WAF (Cloudflare, AWS WAF), do not add a second one. Audit the existing rules instead and report gaps.
**Start the WAF in detection-only mode.** Log what it would have blocked, block nothing. I will review the logs and tell you when to switch to blocking. Do not enable blocking on your own initiative, and do not raise the CRS paranoia level above the default without telling me what it will break.
Step 3 — Remove the fingerprints
Attackers profile a site from what it volunteers. Strip or neutralise all of these:
- `Server` and `X-Powered-By` headers, and any framework or version banner.
- Any load-balancer or session-affinity cookie that encodes an internal address or port. If a cookie must carry routing information, encrypt it. This is exactly how F5 BIG-IP deployments leak internal IPs by default.
- Default error pages and stack traces. Replace with generic pages that reveal no paths, versions, or query text.
- Directory listings, exposed `.git`, `.env`, backup files, and admin panels on predictable paths.
Step 4 — Set the response headers
Add these on every response, at the proxy layer so no application misses them:
- `Strict-Transport-Security` with a long max-age, once HTTPS is confirmed working everywhere.
- `Content-Security-Policy` — build it from what the site actually loads. Deploy it in report-only mode first and show me the violation reports before enforcing.
- `X-Content-Type-Options: nosniff`
- `Referrer-Policy: strict-origin-when-cross-origin`
- `X-Frame-Options: DENY`, or the equivalent CSP frame-ancestors directive.
- `Permissions-Policy` denying camera, microphone, and geolocation unless the site genuinely uses them.
Every cookie the site sets gets `Secure`, `HttpOnly` (unless JavaScript truly needs to read it), and an explicit `SameSite`.
Step 5 — Rate limiting and bots
Apply rate limits at the proxy, tighter on the paths that matter: login, password reset, signup, search, and any API endpoint that costs money or writes data. Return a plain 429 with no detail about why.
Log requests that trip a limit. Do not silently ban IP ranges without telling me.
Step 6 — Logging that a human can read
Send WAF decisions and blocked requests to a structured log — JSON lines, one record per event, with timestamp, source IP, path, matched rule ID, and the action taken. Keep the format identical across all my sites so a single tool can read them all.
Do not log request bodies, passwords, tokens, or personal data.
Step 7 — Verify, then report
After the changes, confirm and show me evidence for each:
- The application is no longer reachable except through the proxy.
- HTTPS works, redirects from HTTP, and the certificate renews automatically.
- The site's own critical paths still work end to end — homepage, login, checkout or form submission, whatever the site does.
- No response header names the server software or its version.
- No cookie decodes to an internal address.
- A harmless test payload (a benign string that matches a CRS rule) appears in the WAF log as a detection.
Then give me a short written summary: what changed, what is now blocked or logged, what I still need to decide, and what would break if the WAF is switched from detection to blocking today.
Rules for the whole job
- Never disable an existing security control to make something work. Tell me about the conflict instead.
- No destructive changes. Back up every config file you edit, and keep a one-command rollback.
- Change one layer at a time and verify the site still works before moving on.
- If any step would take the site down even briefly, stop and tell me first.
---
How to use it
Run it per site, not across all of them at once. Let the first site go all the way through — including the tuning period in detection mode — before you start the second. The rules you learn tuning site one make site two much faster.
The tuning period is the part people skip. The Core Rule Set will flag legitimate traffic on any real application; a week or two of detection-only logs is what tells you which rules to relax for your specific site. Switching straight to blocking is how you end up locking out your own users.
The AI layer, once this is running
Once every site logs in the same JSON format, you have a clean feed to build on:
- **Tuning assistant** — feed a week of detections in, get back which rules are firing on legitimate traffic and what the exclusion should be.
- **Virtual patching** — feed a vulnerability scan result in, get back the WAF rule that blocks that specific exploit, deployed as a stopgap while the code is fixed.
- **Plain-language triage** — turn a day of blocked requests into a few sentences a non-specialist can act on.
That third one is the same unstructured-to-structured problem you already work on, pointed at security logs instead of documents.