| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- # ============================================================
- # 生产环境 Nginx 配置
- # 支持 HTTPS, HTTP/2, 安全 headers, 限流, WebSocket
- # ============================================================
-
- user nginx;
- worker_processes auto;
- worker_rlimit_nofile 65535;
- pid /run/nginx.pid;
- error_log /var/log/nginx/error.log warn;
-
- events {
- worker_connections 4096;
- multi_accept on;
- use epoll;
- }
-
- http {
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- # ==================== 日志格式 ====================
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for" '
- 'rt=$request_time';
-
- log_format json escape=json '{'
- '"time":"$time_iso8601",'
- '"remote_addr":"$remote_addr",'
- '"request":"$request",'
- '"status":$status,'
- '"body_bytes_sent":$body_bytes_sent,'
- '"referer":"$http_referer",'
- '"user_agent":"$http_user_agent",'
- '"request_time":$request_time,'
- '"upstream_response_time":"$upstream_response_time"'
- '}';
-
- access_log /var/log/nginx/access.log json;
-
- # ==================== 基础优化 ====================
- sendfile on;
- tcp_nopush on;
- tcp_nodelay on;
- keepalive_timeout 65;
- keepalive_requests 1000;
- types_hash_max_size 2048;
- server_names_hash_bucket_size 128;
- client_max_body_size 50m;
- client_body_buffer_size 128k;
-
- # ==================== Gzip 压缩 ====================
- gzip on;
- gzip_vary on;
- gzip_proxied any;
- gzip_comp_level 6;
- gzip_buffers 16 8k;
- gzip_http_version 1.1;
- gzip_min_length 1024;
- gzip_types
- text/plain
- text/css
- text/xml
- text/javascript
- application/json
- application/javascript
- application/x-javascript
- application/xml
- application/xml+rss
- application/vnd.ms-fontobject
- font/opentype
- image/svg+xml
- image/x-icon;
-
- # ==================== 限流配置 ====================
- # 全局 API 限流: 每秒 30 个请求
- limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
- # 登录接口限流: 每分钟 10 个请求
- limit_req_zone $binary_remote_addr zone=login_limit:10m rate=10r/m;
- # WebSocket 连接数限制
- limit_conn_zone $binary_remote_addr zone=ws_conn_limit:10m;
-
- # ==================== 上游服务 ====================
- upstream gateway {
- server wm-gateway:8080;
- keepalive 32;
- }
-
- # ==================== HTTP → HTTPS 跳转 ====================
- server {
- listen 80;
- listen [::]:80;
- server_name _;
-
- # Let's Encrypt 验证路径
- location /.well-known/acme-challenge/ {
- root /var/www/certbot;
- allow all;
- }
-
- # 其余请求全部 301 跳转到 HTTPS
- location / {
- return 301 https://$host$request_uri;
- }
- }
-
- # ==================== HTTPS 主服务 ====================
- server {
- listen 443 ssl http2;
- listen [::]:443 ssl http2;
- server_name _;
-
- # ==================== SSL 证书 ====================
- # Let's Encrypt 证书路径(使用 certbot 自动生成)
- ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
- ssl_trusted_certificate /etc/letsencrypt/live/${DOMAIN}/chain.pem;
-
- # SSL 优化
- ssl_session_timeout 1d;
- ssl_session_cache shared:SSL:50m;
- ssl_session_tickets off;
-
- # SSL 协议和加密套件
- 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;
-
- # OCSP Stapling
- ssl_stapling on;
- ssl_stapling_verify on;
- resolver 8.8.8.8 8.8.4.4 valid=300s;
- resolver_timeout 5s;
-
- # ==================== 安全 Headers ====================
- add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
- add_header X-Frame-Options "SAMEORIGIN" always;
- add_header X-Content-Type-Options "nosniff" always;
- add_header X-XSS-Protection "1; mode=block" always;
- add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
- add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' wss: ws: https:; frame-ancestors 'self';" always;
-
- # 隐藏 Nginx 版本
- server_tokens off;
-
- # ==================== 前端静态资源 ====================
- root /usr/share/nginx/html;
- index index.html;
-
- location / {
- try_files $uri $uri/ /index.html;
-
- # HTML 文件不缓存(确保更新及时生效)
- location ~* \.html$ {
- expires -1;
- add_header Cache-Control "no-cache, no-store, must-revalidate";
- }
- }
-
- # 静态资源长期缓存(带 hash 的文件名)
- location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|map)$ {
- expires 1y;
- add_header Cache-Control "public, immutable";
- access_log off;
- }
-
- # ==================== API 反向代理 ====================
- location /api/ {
- limit_req zone=api_limit burst=50 nodelay;
- limit_req_status 429;
-
- proxy_pass http://gateway/;
- proxy_http_version 1.1;
- 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;
- proxy_set_header X-Request-ID $request_id;
- proxy_set_header Connection "";
-
- proxy_connect_timeout 30s;
- proxy_send_timeout 60s;
- proxy_read_timeout 120s;
-
- proxy_buffering on;
- proxy_buffer_size 4k;
- proxy_buffers 8 16k;
- proxy_busy_buffers_size 32k;
-
- # 文件上传大小限制
- client_max_body_size 50m;
- }
-
- # 登录接口严格限流
- location ~* ^/api/(auth|login|oauth) {
- limit_req zone=login_limit burst=5 nodelay;
- limit_req_status 429;
-
- proxy_pass http://gateway;
- proxy_http_version 1.1;
- 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;
- }
-
- # ==================== WebSocket 代理 ====================
- location /ws/ {
- limit_conn ws_conn_limit 10;
-
- proxy_pass http://gateway/ws/;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- 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;
-
- proxy_connect_timeout 7d;
- proxy_send_timeout 7d;
- proxy_read_timeout 7d;
- }
-
- # ==================== MQTT over WebSocket ====================
- location /mqtt {
- proxy_pass http://wm-emqx:8083/mqtt;
- proxy_http_version 1.1;
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
- proxy_set_header Host $host;
- proxy_read_timeout 3600s;
- }
-
- # ==================== GeoServer GIS 代理 ====================
- location /geoserver/ {
- proxy_pass http://wm-geoserver:8080/geoserver/;
- 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;
-
- # GIS 数据可能较大
- client_max_body_size 100m;
- proxy_read_timeout 300s;
- }
-
- # ==================== MinIO 对象存储代理 ====================
- location /minio/ {
- proxy_pass http://wm-minio:9000/;
- 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;
- }
-
- # ==================== 健康检查 ====================
- location /health {
- access_log off;
- return 200 '{"status":"UP"}';
- add_header Content-Type application/json;
- }
-
- # 禁止访问隐藏文件
- location ~ /\. {
- deny all;
- access_log off;
- log_not_found off;
- }
-
- # 错误页面
- error_page 404 /index.html;
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root /usr/share/nginx/html;
- internal;
- }
- }
- }
|