{...}: let
  antiScrape = ''
    @bad_bot {
      header_regexp User-Agent "(?i)(scrapy|cpython-requests|python-requests|curl|wget|go-http-client|ltx71|petalbot|bytespider|dotbot|ahrefsbot|semrushbot|mj12bot|dataforseo|facebookexternalhit|claudebot|anthropic-ai|perplexity|gptbot|chatgpt-user|omnisci|imgproxy|ccbot|exabot|360spider|baiduspider|sogou|duckduckgo|amazonbot|cohere-ai|diffbot|imagesiftbot).*"
    }
    respond @bad_bot "" 444
    header {
      X-Robots-Tag "noindex, nofollow, noai, noimageai"
    }
  '';
in {
  services.caddy = {
    enable = true;
    group = "caddy";
    dataDir = "/var/lib/caddy";
    logDir = "/var/log/caddy";
    globalConfig = ''
      email jory@severijnse.eu
      servers {
        trusted_proxies static private_ranges
      }
    '';
    # Global Caddyfile snippets (shared across all virtual hosts).
    extraConfig = ''
      (admin_gate) {
        @notvpn not remote_ip 10.8.0.0/24
        respond @notvpn "Forbidden" 403
      }
      # Security headers applied to every response of every site that
      # imports this snippet (covers all current and future hosts).
      # For proxied hosts whose upstream sets its own copies, strip them
      # with `header_down` inside the reverse_proxy block instead.
      (security_headers) {
        header {
          X-Content-Type-Options "nosniff"
          X-XSS-Protection "0"
          X-Frame-Options "SAMEORIGIN"
        }
      }
      (csp) {
        header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; connect-src 'self' wss: ws:"
      }
    '';
    virtualHosts = {
      "severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          @logo path /logo.svg
          handle @logo {
            root * /srv
            file_server
            header Content-Type image/svg+xml
            header Cache-Control "public, immutable, max-age=31536000"
            header X-Content-Type-Options nosniff
          }
          handle {
            redir https://jory.severijnse.eu{uri} permanent
          }
        '';
      };

      "www.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          redir https://jory.severijnse.eu{uri} permanent
        '';
      };

      "jory.severijnse.eu" = {
        extraConfig = ''
                    import security_headers
                    import csp
                    ${antiScrape}
                    root * /srv/jory
                    file_server
                    try_files {path} /index.html
                    encode zstd gzip
                    header Strict-Transport-Security "max-age=31536000;"
                    @robots path /robots.txt
                    handle @robots {
                      header Content-Type text/plain
                      respond `User-agent: *
          Allow: /

          User-agent: Googlebot
          Allow: /

          User-agent: Bingbot
          Allow: /

          User-agent: Twitterbot
          Allow: /

          User-agent: facebookexternalhit
          Allow: /

          User-agent: GPTBot
          User-agent: ChatGPT-User
          User-agent: OAI-SearchBot
          User-agent: ClaudeBot
          User-agent: Claude-Web
          User-agent: anthropic-ai
          User-agent: PerplexityBot
          User-agent: Bytespider
          User-agent: Amazonbot
          User-agent: CCBot
          User-agent: Google-Extended
          User-agent: Applebot-Extended
          Disallow: /
          ` 200
                    }
        '';
      };

      "mta-sts.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          root * /srv
          file_server
          header Content-Type text/plain
          header Cache-Control "public, max-age=300"
          header X-Content-Type-Options nosniff
        '';
      };

      "vault.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          import admin_gate
          header Strict-Transport-Security "max-age=31536000;"
          reverse_proxy 127.0.0.1:1001
          encode zstd gzip
        '';
      };

      "git.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          reverse_proxy 127.0.0.1:3000
          encode zstd gzip
        '';
      };

      "mail.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          ${antiScrape}
          reverse_proxy 127.0.0.1:8888 {
            # Strip copies set by the upstream SnappyMail container so we
            # emit exactly one correct value of each security header.
            header_down -X-Frame-Options
            header_down -X-XSS-Protection
            header_down -X-Content-Type-Options
          }
          encode zstd gzip
        '';
      };

      "vpn.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          reverse_proxy 127.0.0.1:51821
          encode zstd gzip
        '';
      };

      "mine.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          reverse_proxy 127.0.0.1:81
        '';
      };

      "music.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          reverse_proxy 127.0.0.1:4321
        '';
      };

      "fail2ban.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          import admin_gate
          reverse_proxy 127.0.0.1:8080
        '';
      };

      "automate.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          import admin_gate
          reverse_proxy 127.0.0.1:5678
          encode zstd gzip
        '';
      };

      "pay.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          ${antiScrape}
          import admin_gate
          reverse_proxy 127.0.0.1:5000
          encode zstd gzip
        '';
      };

      "http://ip.severijnse.eu" = {
        extraConfig = ''
          import security_headers
          import csp
          header Content-Type text/plain
          respond {client_ip} 200
        '';
      };
    };
  };

  systemd.tmpfiles.rules = [
    "d /srv 0755 caddy caddy -"
    "d /srv/jory 0755 caddy caddy -"
  ];
}
