DEV Community

Joseph Anady
Joseph Anady

Posted on • Originally published at thatdevpro.com

Web security headers + HSTS + CSP

Originally published at thatdevpro.com. Part of ThatDevPro's open SEO + AI framework library. ThatDevPro is an SDVOSB-certified veteran-owned web + AI engineering studio. Open-source AI citation toolkit: github.com/Janady13/aio-surfaces.


HTTPS, Headers, Authentication, WAF, Hardening, Incident Response, and the Security Posture Required to Maintain Search Trust

A comprehensive reference for web security across the sites Joseph manages. Security is a baseline expectation in 2026 — Google explicitly considers HTTPS a ranking factor (modest), security issues trigger manual actions, browser warnings destroy conversion rates, and breach incidents create permanent brand damage.


1. Document Purpose

Security is often treated as separate from SEO/marketing concerns, but the disciplines significantly overlap. Hacked sites lose rankings, get flagged in browsers as dangerous, drive away users, generate manual actions in GSC, and require months of recovery work. Sites with poor security signals lose trust both with users and with search engines.

For Joseph's specific situation managing 130+ production client sites on self-managed Linux infrastructure, security discipline is foundational. A single compromised client site affects the broader hosting environment. WordPress sites particularly attract attack attempts continuously.

This framework specifies security implementation across the full stack — server, application, content, and operational dimensions.

1.1 Required Tools

  • SSL Labsssllabs.com/ssltest/ — SSL/TLS configuration testing
  • Mozilla Observatoryobservatory.mozilla.org — security headers analysis
  • Security Headerssecurityheaders.com — header analysis
  • WPScan — WordPress vulnerability scanner
  • OWASP ZAP — application security scanner
  • Wordfence / Sucuri / Patchstack — WordPress security platforms
  • Cloudflare — WAF and DDoS protection
  • Let's Encrypt / acme.sh — free SSL certificates

2. HTTPS Implementation

2.1 SSL Certificate Management

ssl_certificate_management:

  certificate_options:

    lets_encrypt:
      cost: "Free"
      validity: "90 days (auto-renew)"
      best_for: "Most sites; commodity SSL"
      automation: "Certbot, acme.sh"

    paid_dv_certificates:
      cost: "$10-100/year"
      validity: "1-2 years"
      best_for: "Sites preferring longer validity"
      benefit: "Sometimes faster issuance"

    extended_validation:
      cost: "$100-500+/year"
      validity: "1-2 years"
      best_for: "Financial, e-commerce with high trust requirements"
      note: "Browser address bar treatment reduced; less differentiation than years past"

  multi_domain_strategies:

    san_certificates:
      description: "Multiple domains in single certificate"
      use_case: "Multiple related domains"

    wildcard_certificates:
      description: "Covers *.example.com subdomains"
      use_case: "Many subdomains"
      note: "Doesn't cover apex; need separate or SAN with apex"

    multi_san:
      description: "Multiple unrelated domains in one certificate"
      use_case: "Convenience; fewer certificates to manage"
Enter fullscreen mode Exit fullscreen mode

2.2 Let's Encrypt Implementation

For Joseph's Debian/Nginx setup managing 130+ sites:

# Install certbot
apt install certbot python3-certbot-nginx

# Get certificate for single site
certbot --nginx -d example.com -d www.example.com

# Auto-renewal (typically configured by certbot install)
systemctl status certbot.timer

# Manual renewal test
certbot renew --dry-run
Enter fullscreen mode Exit fullscreen mode

For mass renewal management:

# Renew all certificates
certbot renew --quiet

# Should be in cron or systemd timer
# Default: twice daily check, renews when within 30 days of expiry
Enter fullscreen mode Exit fullscreen mode

2.3 Nginx HTTPS Configuration

Strong default Nginx HTTPS configuration:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # SSL Certificate
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Modern SSL Configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    # Security Headers (see Section 3)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # ... rest of configuration
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}
Enter fullscreen mode Exit fullscreen mode

2.4 SSL Testing

After implementation, verify with SSL Labs (ssllabs.com/ssltest/):

  • Target: A or A+ grade
  • Issues to fix: protocol weaknesses, weak ciphers, missing OCSP, certificate chain issues

3. Security Headers

Security headers add layers of browser-enforced protection.

3.1 Complete Security Headers

# Strict-Transport-Security (HSTS)
# Forces HTTPS, prevents downgrade attacks
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# X-Frame-Options
# Prevents clickjacking via iframe embedding
add_header X-Frame-Options "SAMEORIGIN" always;

# X-Content-Type-Options
# Prevents MIME sniffing
add_header X-Content-Type-Options "nosniff" always;

# Referrer-Policy
# Controls referrer information sent
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Permissions-Policy (formerly Feature-Policy)
# Controls browser features
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

# Content-Security-Policy (most complex; see below)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 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://www.google-analytics.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self';" always;

# Cross-Origin policies
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
Enter fullscreen mode Exit fullscreen mode

3.2 Content Security Policy (CSP)

CSP is the most complex but most powerful header. It defines what sources of scripts, styles, images, etc. are allowed:

csp_directives:

  default_src:
    purpose: "Fallback for other directives"
    typical: "'self'"

  script_src:
    purpose: "Allowed JavaScript sources"
    common_additions:
      - "'self'"
      - "https://www.googletagmanager.com"
      - "https://www.google-analytics.com"
      - "'unsafe-inline'" # Often required; reduces protection
    avoid: "'unsafe-eval' unless absolutely required"

  style_src:
    purpose: "Allowed CSS sources"
    common: "'self' 'unsafe-inline' https://fonts.googleapis.com"

  img_src:
    purpose: "Allowed image sources"
    common: "'self' data: https:"

  font_src:
    purpose: "Allowed font sources"
    common: "'self' https://fonts.gstatic.com"

  connect_src:
    purpose: "Allowed fetch/XHR destinations"
    common: "'self' https://www.google-analytics.com"

  frame_ancestors:
    purpose: "Who can iframe this page"
    typical: "'self'"
    note: "Replaces X-Frame-Options"

  base_uri:
    purpose: "Restrict <base> element"
    typical: "'self'"

  form_action:
    purpose: "Where forms can submit"
    typical: "'self'"
Enter fullscreen mode Exit fullscreen mode

3.3 CSP Implementation Approach

csp_implementation:

  step_1_audit:
    - Inventory all script sources
    - Inventory all style sources
    - Inventory all image, font, fetch sources

  step_2_report_only:
    header: "Content-Security-Policy-Report-Only"
    purpose: "Test policy without enforcement"
    duration: "Run 1-2 weeks; collect violations"

  step_3_iterate:
    - Review violation reports
    - Adjust policy to allow legitimate sources
    - Block malicious or unnecessary

  step_4_enforce:
    header: "Content-Security-Policy"
    purpose: "Switch from report-only to enforcement"

  step_5_monitor:
    - Continued violation reporting via report-uri or report-to
    - Adjust as legitimate needs change
Enter fullscreen mode Exit fullscreen mode

4. WordPress-Specific Security

For Joseph's significant WordPress portfolio:

4.1 WordPress Hardening Baseline

wordpress_hardening:

  core_updates:
    requirement: "WordPress core auto-updates enabled"
    cadence: "Weekly verification across portfolio"
    automation: "Automatic for security patches; managed for major versions"

  plugins:
    rule: "Only install reputable plugins from wordpress.org or trusted sources"
    audit: "Quarterly plugin audit per site"
    remove: "Inactive plugins (security surface)"
    update: "Auto-updates enabled for security; managed for major versions"

  themes:
    rule: "Reputable themes only"
    audit: "Inactive themes removed"
    custom_themes: "Maintained and updated"

  user_accounts:
    admin_username: "Never 'admin' (default)"
    default_role: "Subscriber for new users"
    role_review: "Quarterly review of administrators"
    inactive_users: "Removed periodically"

  password_security:
    requirement: "Strong passwords enforced"
    plugin: "Force Strong Passwords or similar"
    two_factor: "Enabled for administrators"

  file_permissions:
    files: "644"
    directories: "755"
    wp_config: "600"

  wp_config_hardening:
    keys: "Salts/keys regenerated; never default"
    db_prefix: "Non-default ('wp_' is default; change to random)"
    debug: "False in production"

  xml_rpc:
    typical: "Disable unless required (mobile app, Jetpack)"
    method: ".htaccess block or plugin"

  rest_api:
    public_endpoints: "Restrict where appropriate"
    plugin: "Disable WP REST API or similar for restriction"

  login_protection:
    rate_limiting: "Limit Login Attempts plugin"
    captcha: "On login form"
    two_factor: "Wordfence 2FA, Google Authenticator, etc."
    custom_login_url: "WPS Hide Login or similar"
Enter fullscreen mode Exit fullscreen mode

4.2 WordPress Security Plugins

wordpress_security_plugin_options:

  wordfence:
    type: "Comprehensive security suite"
    features: ["Firewall", "Malware scan", "Login protection", "2FA"]
    cost: "Free + Premium"
    recommended_for: "Most WordPress sites"

  sucuri:
    type: "Comprehensive security suite"
    features: ["Firewall", "Malware scan", "Cleanup service"]
    cost: "Free plugin + paid services"
    recommended_for: "Sites needing managed cleanup"

  patchstack:
    type: "Vulnerability patching"
    features: ["Virtual patching", "Vulnerability database"]
    cost: "Paid"
    recommended_for: "Sites with many plugins"

  ithemes_security:
    type: "Comprehensive security suite"
    features: ["Various hardening", "2FA", "Log monitoring"]
    cost: "Free + Pro"
    recommended_for: "Alternative to Wordfence"
Enter fullscreen mode Exit fullscreen mode

For Joseph's hosting situation managing 130+ sites: standardizing on one security solution simplifies management. Wordfence Premium across portfolio with central monitoring is common pattern.

4.3 WordPress Backup

wordpress_backup_strategy:

  frequency:
    files: "Daily for high-change sites; weekly for static-content"
    database: "Daily minimum"

  retention:
    daily: "Keep 7-30 days"
    weekly: "Keep 4-12 weeks"
    monthly: "Keep 6-12 months"

  storage:
    requirement: "Off-server location"
    options:
      - S3 / Backblaze B2 / Wasabi
      - Other cloud storage
      - Different physical server
    avoid: "Backups only on same server (lost in compromise)"

  testing:
    quarterly: "Test restore procedure"
    purpose: "Verify backups are actually viable"

  plugins:
    options:
      - UpdraftPlus (most popular)
      - BackWPup
      - Duplicator Pro
      - WP Time Capsule
    server_side:
      - rsync to off-server location
      - Scripted backup to S3 or similar
Enter fullscreen mode Exit fullscreen mode

5. Server-Level Security

For Joseph's self-managed Linux infrastructure:

5.1 SSH Hardening

# /etc/ssh/sshd_config

# Disable root login
PermitRootLogin no

# Use SSH keys only, no passwords
PasswordAuthentication no
ChallengeResponseAuthentication no

# Limit users who can SSH
AllowUsers joseph backupuser

# Change default port (security through obscurity, but reduces noise)
Port 22042  # Or any non-standard port

# Limit authentication attempts
MaxAuthTries 3
LoginGraceTime 30

# Modern protocol only
Protocol 2

# Strong key exchange
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256

# Modern ciphers only
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
Enter fullscreen mode Exit fullscreen mode

After changes:

sshd -t  # Test config
systemctl restart sshd
Enter fullscreen mode Exit fullscreen mode

5.2 Firewall (UFW or iptables)

# Basic UFW configuration
ufw default deny incoming
ufw default allow outgoing

# Allow SSH (on custom port if changed)
ufw allow 22042/tcp

# Allow web traffic
ufw allow 80/tcp
ufw allow 443/tcp

# Enable firewall
ufw enable

# Status
ufw status verbose
Enter fullscreen mode Exit fullscreen mode

5.3 Fail2ban

Prevents brute force attacks:

apt install fail2ban

# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3

[sshd]
enabled = true
port = 22042

[nginx-http-auth]
enabled = true

[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/nginx/access.log
Enter fullscreen mode Exit fullscreen mode

5.4 Automatic Security Updates

# Debian/Ubuntu unattended-upgrades
apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

# Configure /etc/apt/apt.conf.d/50unattended-upgrades
# Enable security updates
# Configure email notifications
Enter fullscreen mode Exit fullscreen mode

5.5 Log Monitoring

log_monitoring:

  what_to_monitor:
    - Failed login attempts (auth.log)
    - Web server errors (nginx error log)
    - Suspicious request patterns (nginx access log)
    - Application errors (varies)

  tools:
    - logwatch (daily summary email)
    - GoAccess (real-time web log analysis)
    - Centralized logging (ELK stack, Graylog, etc.) for larger setups

  alerts:
    - Unusual login patterns
    - High error rates
    - Suspicious traffic patterns
    - Disk space warnings
Enter fullscreen mode Exit fullscreen mode

6. Cloudflare WAF

For sites where Joseph manages or recommends:

cloudflare_security_layer:

  free_tier_features:
    - Free SSL/TLS
    - Basic DDoS protection
    - Bot fight mode
    - Browser integrity check

  pro_tier_additions:
    - WAF rules (managed rulesets)
    - Image optimization
    - Mobile redirect

  business_tier_additions:
    - Custom WAF rules
    - Rate limiting
    - Page rules with more options

  setup_steps:
    - Add domain to Cloudflare
    - Update nameservers
    - Verify proxying enabled (orange cloud)
    - Configure SSL mode (Full Strict recommended)
    - Enable security features
    - Test thoroughly

  custom_rules_examples:
    block_xmlrpc: "Block /xmlrpc.php to all (WordPress)"
    rate_limit_login: "Rate limit /wp-login.php"
    block_known_bad_ips: "Block IPs from threat intelligence"
    challenge_high_risk_countries: "Where business doesn't operate"
Enter fullscreen mode Exit fullscreen mode

7. Application Security

7.1 Common Vulnerabilities (OWASP Top 10)

owasp_top_10_2021:

  A01_broken_access_control:
    examples: "Path traversal, IDOR, missing authorization"
    prevention: "Server-side authorization checks; least privilege"

  A02_cryptographic_failures:
    examples: "Weak encryption, plaintext passwords"
    prevention: "Modern crypto; password hashing (bcrypt, argon2)"

  A03_injection:
    examples: "SQL injection, XSS, command injection"
    prevention: "Parameterized queries; input sanitization; output encoding"

  A04_insecure_design:
    examples: "Missing security in design"
    prevention: "Threat modeling; secure design patterns"

  A05_security_misconfiguration:
    examples: "Default credentials, verbose errors, missing headers"
    prevention: "Security baselines; configuration audits"

  A06_vulnerable_components:
    examples: "Outdated libraries, dependencies"
    prevention: "Dependency scanning; regular updates"

  A07_authentication_failures:
    examples: "Weak passwords, session management issues"
    prevention: "Strong auth, MFA, session security"

  A08_software_data_integrity_failures:
    examples: "Unsigned updates, deserialization vulns"
    prevention: "Signed updates; safe deserialization"

  A09_logging_monitoring_failures:
    examples: "No logging, no monitoring"
    prevention: "Comprehensive logging; alerting"

  A10_ssrf:
    examples: "Server-side request forgery"
    prevention: "URL validation; allow lists"
Enter fullscreen mode Exit fullscreen mode

7.2 Input Validation & Output Encoding

// PHP example — parameterized query (prevents SQL injection)
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);

// JavaScript — output encoding (prevents XSS)
element.textContent = userInput;  // Safe
element.innerHTML = userInput;     // DANGEROUS

// React — automatic encoding
{userInput}  // Safe
dangerouslySetInnerHTML={{__html: userInput}}  // DANGEROUS
Enter fullscreen mode Exit fullscreen mode

8. Incident Response

8.1 Incident Response Plan

incident_response_phases:

  preparation:
    - Documented response plan
    - Contact list (hosting, registrar, security firm)
    - Backup access verified
    - Logging in place to support investigation

  detection:
    - Monitoring alerts
    - User reports
    - Security tools detection
    - Search engine warnings

  containment:
    immediate:
      - Take site offline if active attack
      - Change all credentials
      - Block attacker IP if identified
    short_term:
      - Isolate affected systems
      - Preserve evidence
      - Engage security team or service if needed

  eradication:
    - Identify root cause
    - Remove malicious content
    - Patch vulnerability
    - Verify thorough cleanup

  recovery:
    - Restore from clean backup if needed
    - Bring systems back online
    - Verify normal operation
    - Monitor closely for return

  post_incident:
    - Root cause analysis
    - Documentation
    - Process improvements
    - Affected user notification (if applicable)
Enter fullscreen mode Exit fullscreen mode

8.2 Common Incidents

common_incident_types:

  malware_injection:
    detection: "Malware scan, GSC security warning"
    response: "Identify entry point; clean files; patch vulnerability"

  defacement:
    detection: "Visual inspection; user reports"
    response: "Restore from backup; identify entry point"

  data_breach:
    detection: "Database access patterns; user reports"
    response: "Major incident; legal counsel; user notification"

  ddos:
    detection: "Traffic spike; service degradation"
    response: "Cloudflare or similar; rate limiting"

  account_compromise:
    detection: "Unusual admin activity; user reports"
    response: "Lock account; force password reset; audit logs"
Enter fullscreen mode Exit fullscreen mode

8.3 Manual Action from Hacked Content

If GSC reports manual action for hacked content:

  • Address issue per Section 8.1
  • Verify thorough cleanup
  • Submit reconsideration request via GSC
  • Document remediation work

9. Privacy and Compliance

9.1 Privacy Considerations

privacy_baseline:

  privacy_policy:
    requirement: "Comprehensive policy on every site"
    contents:
      - Data collected
      - How used
      - Third parties
      - User rights
      - Contact information

  cookie_consent:
    requirement: "EEA traffic; good practice elsewhere"
    implementation: "CMP per framework-ga4.md"

  user_data_handling:
    minimum_collection: "Only collect what's needed"
    secure_storage: "Encrypt at rest"
    secure_transmission: "Always HTTPS"
    retention_limits: "Don't keep indefinitely"
    deletion_rights: "Process user deletion requests"
Enter fullscreen mode Exit fullscreen mode

9.2 Regulatory Compliance

relevant_regulations:

  gdpr: "EU and UK; comprehensive privacy law"
  ccpa_cpra: "California; consumer privacy"
  hipaa: "Healthcare data in US"
  pci_dss: "Payment card data"
  ferpa: "Educational records"
  coppa: "Children under 13"
Enter fullscreen mode Exit fullscreen mode

For client sites, compliance is the client's legal responsibility but Joseph's implementation should support compliance (HTTPS, consent, secure handling, etc.).


10. Audit Mode

# Criterion Pass/Fail
SEC1 HTTPS enforced sitewide
SEC2 SSL Labs grade A or A+
SEC3 All security headers configured
SEC4 CSP implemented (at least basic)
SEC5 WordPress hardening baseline applied (if WP)
SEC6 Security plugin active (if WP)
SEC7 Backup strategy implemented and tested
SEC8 SSH hardened (key auth, no root)
SEC9 Firewall configured
SEC10 Fail2ban or equivalent active
SEC11 Automatic security updates enabled
SEC12 Cloudflare or equivalent WAF (where appropriate)
SEC13 Log monitoring established
SEC14 Vulnerability scanning periodic
SEC15 Incident response plan documented
SEC16 Privacy policy current

Score: 16. World-class security baseline: 14+/16.


11. Common Mistakes

  1. HTTP still allowed — must redirect or fail
  2. Missing security headers — easy wins skipped
  3. Default WordPress credentialsadmin username, weak password
  4. Outdated plugins/themes — primary WordPress attack vector
  5. No backup off-server — backup destroyed in compromise
  6. No vulnerability scanning — issues unknown until exploited
  7. No 2FA — account compromise risk
  8. Verbose error messages — leak information to attackers
  9. Default ports and configurations — easier to attack
  10. No incident response plan — paralysis when incident occurs

End of Framework Document

Companion documents:

  • framework-https.md — HTTPS implementation specifics (overlap)
  • framework-hosting.md — Hosting environment security
  • framework-spampolicies.md — Manual actions from hacked content
  • framework-wordpress.md — WordPress-specific implementation
  • framework-serverconfig.md — Server configuration

About this framework library

Dev.to republish of a framework from ThatDevPro's SEO + AI engineering library. Canonical source: https://www.thatdevpro.com/insights/framework-security/

ThatDevPro is an SDVOSB-certified veteran-owned web + AI engineering studio. Need this framework implemented? See the Engine Optimization service or hire via contact.

Top comments (0)