server { listen 80; server_name test.anzhizhichong.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name test.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 安全加固)===== 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 观察(只上报不拦截) 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/pms_dev; index index.html; # 登录接口限流(防暴力破解第二道;第一道在 api-server 内按手机号+IP 计数锁定) location = /api/pms/auth/login { limit_req zone=auth_login_zone burst=10 nodelay; proxy_pass http://127.0.0.1:3000/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:3000/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:3000/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:3000/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; } # 上传文件(与生产一致直出;此前测试站缺此 location,/uploads/ 会落到 root 变 404) location /uploads/ { limit_req zone=uploads_zone burst=60 nodelay; proxy_pass http://127.0.0.1:3000/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; } location /pms/ { alias /data/pms_dev/web/; try_files $uri $uri/ /pms/index.html; } location /h5/ { alias /data/pms_dev/h5/; try_files $uri $uri/ /h5/index.html; } }