Security
Security layers from Cloudflare edge rules to WordPress application hardening.
Security is handled at multiple layers — Cloudflare at the edge, Nginx at the web server, and the yax-security mu-plugin at the application level.
Cloudflare (Edge)
WAF / Security Rules
| Order | Rule | Match | Action |
|---|---|---|---|
| 1 | Protect Login | URI starts with /wp-login.php | Managed Challenge |
| 2 | BrightEdge | IP source in BrightEdge range | Skip (allow crawler) |
| 3 | Allow Facebook | AS Num 32934 | Skip (disabled) |
| 4 | Block non-USA origin | Country is not US and not CA | Block |
| 5 | Bypass WAF for health | URI starts with /healthz | Skip |
IP Access Rules
IP-level allow/block rules are managed in the Cloudflare dashboard under Security → WAF → IP access rules. These are used to allowlist monitoring services and block known bad actors.
Geo-Blocking
Non-US and non-CA traffic is blocked at the edge (rule 4). This is a healthcare site serving patients in the Pacific Northwest — international traffic is almost entirely bot or attack traffic.
Turnstile
Cloudflare Turnstile provides bot protection on forms via the simple-cloudflare-turnstile WordPress plugin. It replaces traditional CAPTCHAs with a non-intrusive challenge that doesn't require user interaction in most cases.
SSL
SSL is terminated at Cloudflare (proxied mode). The connection between Cloudflare and Heroku uses Heroku's ACM (Automated Certificate Management). WordPress detects HTTPS via the X-Forwarded-Proto header passed through the proxy chain.
Nginx (Web Server)
Nginx enforces several security rules in both nginx.conf (production) and nginx.dev.conf (local dev):
| Rule | What it blocks |
|---|---|
location ~ /\. | Dotfiles (.env, .git, .htaccess) |
| `location ~ ^/(wp-config.php | readme.html |
location ~* /(?:uploads|files)/.*\.php$ | PHP execution in upload directories |
location = /xmlrpc.php | XML-RPC endpoint (common attack vector) |
Security headers set by Nginx:
| Header | Value |
|---|---|
X-Frame-Options | SAMEORIGIN |
X-XSS-Protection | 1; mode=block |
X-Content-Type-Options | nosniff |
Referrer-Policy | no-referrer-when-downgrade |
WordPress Application
yax-security Plugin
The yax-security mu-plugin provides application-level hardening with configuration via an options page in wp-admin. Capabilities include disabling file editing, hiding WordPress version info, and other standard hardening measures.
yax-cleanup Plugin
The yax-cleanup mu-plugin removes WordPress features that expose attack surface:
- Emoji scripts and styles (reduces fingerprinting)
- oEmbed discovery links
- WordPress generator meta tag (version disclosure)
- Windows Live Writer manifest
- RSD (Really Simple Discovery) link
AJAX Nonce Verification
All AJAX endpoints use WordPress nonce verification. The theme generates a nonce via wp_create_nonce('wha_ajax') and passes it to JavaScript through wp_localize_script. AJAX handlers verify with check_ajax_referer().
Output Escaping
Template output uses WordPress escaping functions (esc_html(), esc_attr(), wp_kses_post()) throughout. The production readiness audit (2026-02) identified and fixed unescaped output in utility functions and templates.
Build-Time and Deploy Hardening
The build script strips info-disclosure files (readme.html, license.txt), and dev-only plugins are physically removed on every container start. See Deployment and Plugin System.
Health Check
The /healthz endpoint (provided by healthz.php mu-plugin) returns a simple 200 response for uptime monitoring. It bypasses both Cloudflare WAF rules and cache rules to ensure accurate monitoring.