0%

Nginx 前端部署——静态资源服务与反向代理配置

Nginx 是一款高性能的 HTTP 和反向代理服务器,广泛应用于前端应用的部署与优化。本文将详细介绍 Nginx 在前端部署中的各种配置技巧,包括 Vue 和 React 单页应用的路由处理、反向代理设置、负载均衡策略等,帮助前端开发者更好地利用 Nginx 提升应用性能和用户体验。

介绍

  Nginx(发音为”engine-x”)是一款高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。在现代前端开发中,Nginx 扮演着至关重要的角色,特别是在部署单页应用(SPA)、处理跨域问题、实现负载均衡和 SSL 终止等方面。

  对于前端开发者而言,掌握 Nginx 配置不仅可以帮助我们更好地部署应用,还能优化性能、处理路由问题,实现更优雅的部署方案。

Nginx 基础概念

Nginx 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# nginx.conf - 基础配置结构
# 全局配置块 user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# 事件配置块 events {
worker_connections 1024;
use epoll;
}

# HTTP 配置块 http {
# MIME 类型定义 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"';

access_log /var/log/nginx/access.log main;

# 全局 HTTP 设置 sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

# Gzip 压缩 gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/Css
text/Xml
text/Javascript
application/Json
application/Javascript
application/Xml+rss;

# 包含服务器配置 include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

核心指令解释

1
# 重要指令说明 worker_processes    # 工作进程数,通常设置为 CPU 核心数 worker_connections  # 每个进程的最大连接数 keepalive_timeout   # Keep-Alive 连接超时时间 gzip on            # 启用 Gzip 压缩 sendfile on        # 启用 sendfile 零拷贝优化

Nginx 与前端应用部署

静态文件服务配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 基础静态文件服务配置 server {
listen 80;
server_name example.com www.example.com;

# 根目录配置 root /var/www/Html/my-frontend-app;
index index.Html index.htm;

# 静态资源优化 location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
log_not_found off;
}

# 字体文件缓存 location ~* \.(woff|woff2|ttf|eot)$ {
expires 1y;
add_header Access-Control-Allow-Origin *;
}

# 默认页面 location / {
try_files $uri $uri/ /index.Html;
}

# 错误页面配置 error_page 404 /404.Html;
error_page 500 502 503 504 /50x.Html;
}

Vue 应用部署配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Vue 应用部署配置 server {
listen 80;
server_name Vue.example.com;

# Vue 应用根目录 root /var/www/Html/Vue-app/dist;
index index.Html;

# Vue Router History 模式配置 location / {
try_files $uri $uri/ /index.Html;
}

# API 代理配置(如果前端需要调用后端 API)
location /API/ {
proxy_pass http://localhost:3000/;
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_cache_bypass $http_upgrade;

# 设置代理超时 proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}

# 静态资源缓存 location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}

# 防止访问隐藏文件 location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}

React 应用部署配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# React 应用部署配置 server {
listen 80;
server_name React.example.com;

# React 应用根目录 root /var/www/Html/React-app/build;
index index.Html;

# React Router BrowserRouter 配置 location / {
try_files $uri $uri/ /index.Html;
}

# 静态资源优化 location ~* \.(JS|Css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
gzip_static on;
}

location ~* \.(png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}

# API 请求代理 location ^~ /API/ {
proxy_pass http://backend-service:3000/;
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;

# CORS 头 add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range";

# 处理预检请求 if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
}

# 健康检查端点 location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}

SPA 路由配置详解

Hash 模式 vs History 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Vue Router 配置示例 import { createRouter, createWebHashHistory, createWebHistory } from 'Vue-router';

// Hash 模式 - 无需特殊 Nginx 配置 const routerHash = createRouter({
history: createWebHashHistory(),
routes: [
// 路由配置
]
});

// History 模式 - 需要 Nginx 配置支持 const routerHistory = createRouter({
history: createWebHistory(),
routes: [
// 路由配置
]
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# History 模式 Nginx 配置 server {
listen 80;
server_name spa.example.com;

root /var/www/Html/spa-app;
index index.Html;

# 关键配置:捕获所有路由并返回 index.Html
location / {
try_files $uri $uri/ /index.Html;
}

# 静态资源单独处理 location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

路由配置最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 完整的 SPA 路由配置示例 server {
listen 80;
server_name myapp.com;
return 301 https://$server_name$request_uri; # HTTP 重定向到 HTTPS
}

server {
listen 443 ssl http2;
server_name myapp.com;

# SSL 配置 ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384;

root /var/www/Html/myapp/dist;
index index.Html;

# SPA 路由处理 location / {
try_files $uri $uri/ @fallback;
}

# 回退到 index.Html 处理 location @fallback {
rewrite ^.*$ /index.Html last;
}

# API 代理 location /API/ {
proxy_pass http://API-backend: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;
proxy_set_header X-Forwarded-Proto $scheme;
}

# 静态资源缓存 location ~* \.(JS|Css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
gzip on;
gzip_types text/Css application/Javascript;
}

location ~* \.(png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}

# 压缩 Html
location ~* \.Html$ {
gzip on;
gzip_types text/Html;
}

# 安全头 add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

多应用部署方案

子路径部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 在同一域名下部署多个应用 server {
listen 80;
server_name example.com;

# 主应用 location / {
alias /var/www/Html/main-app/;
try_files $uri $uri/ @fallback_main;
}

location @fallback_main {
rewrite ^.*$ /index.Html last;
}

# 博客应用 location /blog/ {
alias /var/www/Html/blog-app/;
try_files $uri $uri/ @fallback_blog;
}

location @fallback_blog {
rewrite ^.*$ /blog/index.Html last;
}

# 管理后台 location /admin/ {
alias /var/www/Html/admin-app/;
try_files $uri $uri/ @fallback_admin;
}

location @fallback_admin {
rewrite ^.*$ /admin/index.Html last;
}

# API 代理 location /API/ {
proxy_pass http://API-server:3000/;
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;
}
}

子域名部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 使用子域名部署不同应用 server {
listen 80;
server_name app1.example.com;

root /var/www/Html/app1/dist;
index index.Html;

location / {
try_files $uri $uri/ /index.Html;
}

location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

server {
listen 80;
server_name app2.example.com;

root /var/www/Html/app2/build;
index index.Html;

location / {
try_files $uri $uri/ /index.Html;
}

location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

# API 网关 server {
listen 80;
server_name API.example.com;

location / {
proxy_pass http://internal-API: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;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

反向代理配置

基础反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 基础反向代理配置 upstream backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}

server {
listen 80;
server_name API.example.com;

location / {
proxy_pass http://backend;

# 代理头设置 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 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;

# 缓冲设置 proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
}

高级代理配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 高级代理配置 upstream app_backend {
# 负载均衡策略 least_conn; # 最少连接数 server 192.168.1.10:8080 weight=3 max_fails=2 fail_timeout=30s;
server 192.168.1.11:8080 weight=2 max_fails=2 fail_timeout=30s;
server 192.168.1.12:8080 weight=1 max_fails=2 fail_timeout=30s backup;
}

server {
listen 443 ssl http2;
server_name API.example.com;

ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;

# API 限流 limit_req_zone $binary_remote_addr zone=API:10m rate=10r/s;

location /API/ {
# 应用限流 limit_req zone=API burst=20 nodelay;

proxy_pass http://app_backend;

# WebSocket 支持 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 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;

# 缓冲设置 proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;

# 压缩设置 proxy_compression on;
proxy_compression_types text/plain text/Css application/Json application/Javascript text/Xml application/Xml;
}

# 健康检查 location /health {
access_log off;
return 200 '{"status":"ok","timestamp":'$(date +%s)'}\n';
add_header Content-Type application/Json;
}
}

负载均衡策略

负载均衡算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 不同的负载均衡策略 upstream balanced_backend {
# 1. 轮询(默认)
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}

upstream weighted_backend {
# 2. 加权轮询 server backend1.example.com:8080 weight=3;
server backend2.example.com:8080 weight=2;
server backend3.example.com:8080 weight=1;
}

upstream ip_hash_backend {
# 3. IP 哈希(会话保持)
ip_hash;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}

upstream least_conn_backend {
# 4. 最少连接 least_conn;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}

upstream fair_backend {
# 5. 响应时间优先(需要 fair 模块)
fair;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}

# 配置使用特定策略的服务器 server {
listen 80;
server_name loadbalanced.example.com;

location / {
proxy_pass http://weighted_backend; # 使用加权轮询 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;
}
}

健康检查配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 第三方健康检查模块配置(如 nginx_upstream_check_module)
upstream backend_with_healthcheck {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;

# 健康检查配置(需要第三方模块)
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "GET /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

# 或者使用简单的错误处理 upstream simple_backend {
server backend1.example.com:8080 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 max_fails=3 fail_timeout=30s;
server backend3.example.com:8080 max_fails=3 fail_timeout=30s backup;
}

安全配置

安全头配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 安全相关的头部配置 server {
listen 443 ssl http2;
server_name secure.example.com;

# SSL 配置 ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;

# SSL 安全设置 ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# 安全头部 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 Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

location / {
root /var/www/Html/secure-app;
try_files $uri $uri/ /index.Html;

# 对于 API 请求添加额外的安全头 location ~ ^/API/ {
proxy_pass http://backend;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
}
}
}

访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 访问控制配置 server {
listen 80;
server_name restricted.example.com;

# IP 白名单 location / {
# 允许特定 IP 访问 allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;

root /var/www/Html/restricted-app;
try_files $uri $uri/ /index.Html;
}

# 限制特定路径的访问 location /admin {
# 基本身份验证 auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;

# 同时限制 IP
allow 192.168.1.100;
deny all;

root /var/www/Html/admin-panel;
try_files $uri $uri/ /index.Html;
}

# API 访问限制 location /API/ {
# 限制请求频率 limit_req zone=API burst=10 nodelay;

# 限制连接数 limit_conn addr 10;

proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

性能优化

缓存配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 缓存配置 proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;

server {
listen 80;
server_name cached.example.com;

# 静态资源长期缓存 location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}

# Html 文件短期缓存 location ~* \.Html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}

# API 响应缓存 location /API/ {
proxy_pass http://backend;

# 启用缓存 proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_bypass $http_cache_control;

# 缓存头部 add_header X-Cache-Status $upstream_cache_status;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# 页面缓存 location / {
root /var/www/Html/cached-app;
try_files $uri $uri/ /index.Html;

# 对于某些动态页面启用缓存 set $no_cache 0;

# 如果有认证 cookie 则不缓存 if ($http_cookie ~* "auth=") {
set $no_cache 1;
}

proxy_cache_bypass $no_cache;
proxy_no_cache $no_cache;
}
}

压缩和优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 压缩和性能优化配置 server {
listen 80;
server_name optimized.example.com;

# Gzip 压缩设置 gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types
text/plain
text/Css
text/Xml
text/Javascript
application/Javascript
application/Xml+rss
application/Json
application/atom+Xml
application/rss+Xml
image/svg+Xml;

# Brotli 压缩(如果编译了 brotli 模块)
brotli on;
brotli_comp_level 6;
brotli_types
text/plain
text/Css
text/Xml
text/Javascript
application/Javascript
application/Xml+rss
application/Json;

# 静态文件优化 location ~* \.(JS|Css)$ {
expires 1y;
add_header Cache-Control "public, immutable";

# 启用 gzip_static 或 brotli_static
gzip_static on;
brotli_static on;
}

location / {
root /var/www/Html/optimized-app;
try_files $uri $uri/ /index.Html;

# 启用 sendfile
sendfile on;
tcp_nopush on;
tcp_nodelay on;

# 连接优化 keepalive_timeout 65;
keepalive_requests 100;
}
}

Docker 集成配置

Docker Compose 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# docker-compose.yml - Nginx 与前端应用集成 version: '3.8'

services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
- ./Html:/var/www/Html:ro
depends_on:
- frontend
- backend
networks:
- app-network

frontend:
build:
context: ./frontend
dockerfile: Dockerfile
volumes:
- ./frontend/dist:/usr/share/nginx/Html
networks:
- app-network

backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "3000:3000"
networks:
- app-network

networks:
app-network:
driver: bridge

Nginx Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Dockerfile - 自定义 Nginx 镜像 FROM nginx:alpine

# 复制配置文件 COPY nginx.conf /etc/nginx/nginx.conf
COPY conf.d/ /etc/nginx/conf.d/
COPY ssl/ /etc/nginx/ssl/

# 复制前端应用 COPY dist/ /var/www/Html/

# 创建日志目录 RUN mkdir -p /var/log/nginx

# 暴露端口 EXPOSE 80 443

# 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/health || exit 1

CMD ["nginx", "-g", "daemon off;"]

监控和日志

日志配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 详细的日志配置 log_format detailed_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';

server {
listen 80;
server_name logged.example.com;

# 访问日志 access_log /var/log/nginx/access.log detailed_log;
error_log /var/log/nginx/error.log warn;

location / {
root /var/www/Html/logged-app;
try_files $uri $uri/ /index.Html;
}

# API 请求单独记录 location /API/ {
access_log /var/log/nginx/api_access.log detailed_log;
error_log /var/log/nginx/api_error.log;

proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

监控配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Nginx 状态监控配置 server {
listen 80;
server_name monitoring.example.com;

# Nginx 状态页面 location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}

# 自定义健康检查端点 location /health {
access_log off;
return 200 '{"status":"healthy","timestamp":'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'}\n';
add_header Content-Type application/Json;
add_header Cache-Control "no-cache";
}

# 应用主页 location / {
root /var/www/Html/monitoring-app;
try_files $uri $uri/ /index.Html;
}
}

常见问题解决

SPA 路由问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 解决 Vue/React 路由刷新404问题 server {
listen 80;
server_name spa-fix.example.com;

root /var/www/Html/spa-app;

# 方法1: try_files
location / {
try_files $uri $uri/ /index.Html;
}

# 方法2: 正则表达式匹配非文件请求 location ~* ^/(?!API|assets|static).* {
try_files $uri $uri/ /index.Html;
}

# 静态资源(确保这些路径不被 SPA 路由捕获)
location ~* \.(JS|Css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}

CORS 问题解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# CORS 配置 server {
listen 80;
server_name cors.example.com;

location / {
root /var/www/Html/frontend-app;

# 添加 CORS 头 add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";
add_header Access-Control-Max-Age 1728000;

# 处理预检请求 if ($request_method = 'OPTIONS') {
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}

try_files $uri $uri/ /index.Html;
}

# API 代理的 CORS 配置 location /API/ {
proxy_pass http://backend-API:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# API 级别的 CORS
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization";

if ($request_method = 'OPTIONS') {
add_header Content-Length 0;
return 204;
}
}
}

最佳实践总结

部署清单

1
2
3
4
5
6
7
8
9
10
11
# Nginx 部署检查清单
□ SSL 证书配置正确
□ SPA 路由配置正确(try_files 指令)
□ 静态资源缓存配置
□ 安全头部设置
□ 日志配置
□ 错误页面配置
□ Gzip 压缩启用
□ 防火墙规则设置
□ 监控配置
□ 备份策略

性能优化清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 性能优化配置 server {
# 基础优化 sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;

# 缓存策略 location ~* \.(JS|Css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}

# 连接优化 location / {
try_files $uri $uri/ /index.Html;

# 限制并发连接 limit_conn addr 100;

# 限制请求频率 limit_req zone=general burst=10 nodelay;
}
}

总结

  • Nginx 是前端部署不可或缺的工具,特别适合部署单页应用
  • SPA 的 History 模式需要特殊的路由配置来处理客户端路由
  • 合理的缓存策略能显著提升前端应用性能
  • 反向代理和负载均衡功能使得 Nginx 成为优秀的 API 网关
  • 安全配置是生产环境的必要考虑因素
  • Docker 集成使得部署更加标准化和可移植
  • 日志和监控配置有助于问题排查和性能优化

春节期间,就像 Nginx 配置要把多个应用”节目”整合在一起一样,每个应用都有自己的位置和配置,最终形成一个完整的”联欢晚会”。Nginx 就像这场联欢晚会的导演,协调各方资源,确保观众获得最好的体验。

扩展阅读

  1. Nginx 官方文档
  2. Nginx 配置最佳实践
  3. 前端部署指南
  4. SPA 部署最佳实践

练习建议

  1. 配置一个 Vue 应用的 Nginx 服务器
  2. 实现 React 应用的 History 模式路由
  3. 设置 API 反向代理和负载均衡
  4. 配置 SSL 证书和安全头部
  5. 实现多应用部署方案
bulb