server { listen 80; server_name app.anzhizhichong.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name app.anzhizhichong.com; ssl_certificate /etc/nginx/ssl/app.anzhizhichong.com.pem; ssl_certificate_key /etc/nginx/ssl/app.anzhizhichong.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # ===== 安全响应头(2026-09 安全加固)===== # HSTS:本域强制 HTTPS(暂不加 includeSubDomains,避免影响未知子域) add_header Strict-Transport-Security "max-age=31536000" always; # 禁止 MIME 嗅探:/uploads 免认证同源直出,防上传内容被当 HTML/JS 执行 add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # CSP 先 Report-Only 观察(只上报不拦截),确认 SPA 无违规后再考虑收紧为强制 add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always; # 产品介绍页 root /data/landing; index index.html; location /doc/ { proxy_pass http://127.0.0.1:3080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 500m; } # 登录接口限流(防暴力破解第二道;第一道在 api-server 内按手机号+IP 计数锁定) location = /api/pms/auth/login { limit_req zone=auth_login_zone burst=10 nodelay; proxy_pass http://127.0.0.1:9000/api/auth/login; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 验证码接口限流(免认证且逐像素渲染,是 CPU 放大攻击面) location = /api/pms/auth/challenge { limit_req zone=auth_challenge_zone burst=10 nodelay; proxy_pass http://127.0.0.1:9000/api/auth/challenge; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 扫码入口(免认证,协议要求:扫二维码 → abstract_id 查柜子) # 限流依据:abstract_id 形如 `10-00000001`,可被脚本枚举(能判断柜子是否存在、 # 柜名与仓体结构)。正常扫码是"一次扫码 1~2 个请求"的高频正常行为, # 故阈值比登录/验证码宽松得多:30r/m 持续 + burst=20 瞬时 # (同一 IP 连扫 20 次不排队),429 只可能来自脚本枚举/刷取。 location /api/pms/cabinets/abstract/ { limit_req zone=scan_abstract_zone burst=20 nodelay; proxy_pass http://127.0.0.1:9000/api/cabinets/abstract/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /api/pms/ { proxy_pass http://127.0.0.1:9000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 10m; } # 头像上传文件(免认证同源直出)——限流;nosniff 由 server 级继承 location /uploads/ { limit_req zone=uploads_zone burst=60 nodelay; proxy_pass http://127.0.0.1:9000/uploads/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; expires 30d; } # PMS管理前端 - 不带斜杠时重定向 location = /pms { return 301 /pms/; } # PMS管理前端 location /pms/ { alias /data/pms/web/; try_files $uri $uri/ /pms/index.html; } }