{ admin off servers { # TLS 由前面的 Nginx Proxy Manager 终止,转发进来是明文 http。 # 只信任内网来源的 X-Forwarded-For,{client_ip} 才解析得出学生的真实 IP。 trusted_proxies static private_ranges } } # 站点地址只写端口,Caddy 就不会去申请证书(auto-HTTPS 自动关闭)。 :8000 { encode gzip # 和 NPM 那一层的 client_max_body_size 保持一致,上传测试用例压缩包用得上。 # 必须写 MiB:Caddy 的 MB 按 10^6 算,200MB 比 nginx 的 200M 小了将近 10M。 request_body { max_size 200MiB } # Caddy 的 header 会跨层叠加,不像 nginx 的 add_header 一旦在子块出现就不再继承, # 所以这里写一次就够,下面各 handle 里只管自己的 Cache-Control。 header { X-XSS-Protection "1; mode=block" X-Frame-Options SAMEORIGIN X-Content-Type-Options nosniff } log { output file /data/log/caddy_access.log { roll_size 10MiB roll_keep 10 } } # 判题机心跳每秒一次;静态资源量大且带永久缓存。两者都不记访问日志。 @nolog path /api/judge_server_heartbeat/ /assets/* log_skip @nolog handle /api/* { reverse_proxy 127.0.0.1:8080 { header_up X-Real-IP {client_ip} # AI 分析走 SSE。X-Accel-Buffering 是 nginx 专有的,Caddy 不认, # 这里显式关掉响应缓冲,保证流式输出逐块下发。 flush_interval -1 } } handle /ws/* { reverse_proxy 127.0.0.1:8080 { header_up X-Real-IP {client_ip} } } # 头像、上传文件、站点图标,实际落在 /data/public 下。 handle /public/* { root * /data file_server } # 构建产物文件名带内容 hash,内容一变文件名就变,可以永久缓存。 # 命中后浏览器直接读磁盘,不再发条件请求。 handle /assets/* { root * /app/dist header Cache-Control "public, max-age=31536000, immutable" file_server } # index.html 引用着带 hash 的文件名,必须每次回源校验, # 否则发新版后学生刷不到。no-cache 是"缓存但每次校验",命中走 304。 handle { root * /app/dist header Cache-Control "no-cache" try_files {path} /index.html file_server } }