寫了很多前端代碼,但部署環節經常依賴運維同學。這次自己動手配了一個 nginx,把常見場景整理一下。
基礎:託管 SPA
nginx
server {
listen 80;
server_name example.com;
root /var/www/myapp/dist;
index index.html;
# SPA 關鍵配置:所有路徑都返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
}
try_files $uri $uri/ /index.html 的意思:
- 先找文件
$uri(如/static/main.js) - 再找目錄
$uri/ - 都沒有則返回
/index.html(交給前端路由處理)
緩存配置
HTML:不緩存
nginx
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires 0;
}
帶 hash 的靜態資源:長期緩存
nginx
location ~* \.(js|css)$ {
# 文件名帶 hash(如 app.a1b2c3d4.js),可以永久緩存
add_header Cache-Control "public, max-age=31536000, immutable";
}
location ~* \.(png|jpg|jpeg|gif|svg|ico|woff|woff2)$ {
add_header Cache-Control "public, max-age=2592000"; # 30天
}
gzip 壓縮
nginx
http {
gzip on;
gzip_vary on;
gzip_min_length 1024; # 小於 1KB 不壓縮
gzip_comp_level 6; # 壓縮級別 1-9,6 是速度和壓縮率的平衡
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
}
如果構建時已生成 .gz 文件,可以直接使用,省去運行時壓縮:
nginx
# Webpack 開啓 compression-webpack-plugin 生成 .gz 文件
location ~* \.(js|css)$ {
gzip_static on; # 優先使用 .gz 靜態文件
add_header Cache-Control "public, max-age=31536000, immutable";
}
HTTPS 配置
nginx
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri; # HTTP 重定向到 HTTPS
}
server {
listen 443 ssl http2; # 開啓 HTTP/2
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 安全配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
# HSTS:強制瀏覽器用 HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
root /var/www/myapp/dist;
# ... 其餘配置
}
API 代理
前後端分離,前端直接請求後端 API 會有跨域問題,用 nginx 代理解決:
nginx
server {
# 前端資源
location / {
root /var/www/frontend/dist;
try_files $uri $uri/ /index.html;
}
# API 請求代理到後端
location /api/ {
proxy_pass http://localhost:8080/; # 轉發到後端服務
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
安全響應頭
nginx
# 在 server 塊或 http 塊裏添加
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'" always;
完整配置示例
nginx
server {
listen 443 ssl http2;
server_name myapp.example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
root /var/www/myapp/dist;
# 安全頭
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
# index.html - 不緩存
location = /index.html {
add_header Cache-Control "no-cache";
}
# 帶 hash 的靜態資源 - 長期緩存
location ~* \.(js|css)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
gzip_static on;
}
# 圖片
location ~* \.(png|jpg|jpeg|gif|svg|ico|woff2)$ {
add_header Cache-Control "public, max-age=2592000";
}
# SPA 路由
location / {
try_files $uri $uri/ /index.html;
}
# API 代理
location /api/ {
proxy_pass http://localhost:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
小結
- SPA 必須配
try_files ... /index.html - HTML 不緩存,帶 hash 的資源永久緩存
- 開 gzip 壓縮,JS 通常能壓縮 70%
- HTTPS + HTTP/2 是現代標配