Skip to content

Nginx

安装 Nginx

  • Ubuntu / Debian
shell
sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx
nginx -v
  • CentOS / Rocky / Alma (dnf/yum)
shell
sudo dnf install -y nginx   # 或 yum install -y nginx
sudo systemctl enable --now nginx
nginx -v

基本目录与配置结构

  • 常见路径:

    • 主配置:/etc/nginx/nginx.conf
    • 站点配置:
      • Debian/Ubuntu:/etc/nginx/sites-available/ + sites-enabled/
      • CentOS系:/etc/nginx/conf.d/*.conf
    • 日志:/var/log/nginx/access.log/var/log/nginx/error.log
    • 默认站点目录:/usr/share/nginx/html/var/www/html
  • nginx.conf

nginx
user  nginx;
worker_processes auto;

events {
  worker_connections 1024;
}

http {
  include       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"';

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

  sendfile        on;
  keepalive_timeout  65;

  include /etc/nginx/conf.d/*.conf;   # 或 include sites-enabled/*
}

基本配置:静态站点 / 反向代理

静态站点(前端 dist)

nginx
server {
  listen 80;
  server_name example.com;

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

  location / {
    try_files $uri $uri/ /index.html;  # SPA 必备
  }
}

反向代理(把 80 转到后端服务)

nginx
server {
  listen 80;
  server_name api.example.com;

  location / {
    proxy_pass http://127.0.0.1:3000;
    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 需要
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}

负载均衡配置(upstream)

最常用:轮询(默认)

nginx
upstream backend_pool {
  server 10.0.0.11:3000;
  server 10.0.0.12:3000;
  server 10.0.0.13:3000;
}

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

  location / {
    proxy_pass http://backend_pool;
  }
}

加权:

nginx
upstream backend_pool {
  server 10.0.0.11:3000 weight=5;
  server 10.0.0.12:3000 weight=3;
  server 10.0.0.13:3000 weight=1;
}

IP 哈希(同一用户尽量命中同一台:会话粘性的一种简易方式)

nginx
upstream backend_pool {
  ip_hash;
  server 10.0.0.11:3000;
  server 10.0.0.12:3000;
}

最少连接(适合耗时请求更均匀)

nginx
upstream backend_pool {
  least_conn;
  server 10.0.0.11:3000;
  server 10.0.0.12:3000;
}

健康/失败参数(常见写法)

nginx
upstream backend_pool {
  server 10.0.0.11:3000 max_fails=3 fail_timeout=10s;
  server 10.0.0.12:3000 max_fails=3 fail_timeout=10s;
}
  • max_fails:连续失败多少次认为不可用
  • fail_timeout:失败统计窗口 + 暂时剔除的时间

配置说明

  • server {}:一个虚拟主机
  • listen 80;:监听端口
  • server_name:域名匹配(可多个)
  • location /path {}:路径匹配块(前缀/正则)
  • root vs alias
    • root /a; location /img/ {} → 实际路径 /a/img/...
    • alias /a/img/; location /img/ {} → 实际路径 /a/img/...(注意结尾 / 影响很大)
  • try_files:文件存在就直接返回,否则兜底(SPA 常用)
  • proxy_pass:转发到上游
  • include:拆分配置

SSL 配置(HTTPS)

基础 HTTPS(证书已准备好)

nginx
server {
  listen 443 ssl;
  server_name example.com;

  ssl_certificate     /etc/nginx/certs/fullchain.pem;
  ssl_certificate_key /etc/nginx/certs/privkey.pem;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;

  location / {
    root /var/www/myapp/dist;
    try_files $uri $uri/ /index.html;
  }
}

# HTTP 自动跳转 HTTPS
server {
  listen 80;
  server_name example.com;
  return 301 https://$host$request_uri;
}

Let’s Encrypt(自动签证书)

  • DNS 必须有 A 记录
shell
dig example.com +short
dig www.example.com +short
  • 防火墙(如启用 ufw):
shell
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
  • 用 conf.d 写一个“可验证的 HTTP 站点”

    Certbot nginx 插件要能找到匹配 server_nameserver {} 才好自动改配置

  • 创建配置文件:
shell
sudo nano /etc/nginx/conf.d/example.com.conf
  • 写入(先只配 HTTP):
nginx
# /etc/nginx/conf.d/example.com.conf

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    # 方式 1:静态站点
    root /var/www/example.com;
    index index.html;

    # Let’s Encrypt 验证文件目录(可选但推荐显式写)
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/example.com;
        default_type "text/plain";
        try_files $uri =404;
    }

    location / {
        try_files $uri $uri/ =404;
    }
}
  • 创建站点目录与测试页:
shell
sudo mkdir -p /var/www/example.com/.well-known/acme-challenge
echo "ok" | sudo tee /var/www/example.com/index.html >/dev/null
sudo chown -R www-data:www-data /var/www/example.com
  • 检查并重载:
shell
sudo nginx -t
sudo systemctl reload nginx
  • 验证 HTTP 是否能访问:
shell
curl -I http://example.com
  • 安装 Certbot(建议 snap,避免 Python 依赖冲突)
shell
sudo apt remove -y certbot python3-certbot-nginx
sudo apt autoremove -y

sudo apt update
sudo apt install -y snapd
sudo snap install core
sudo snap refresh core

sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
  • 签发证书(自动修改 conf.d 里的 server 块)
shell
sudo certbot --nginx -d example.com -d www.example.com
  • 会自动改 Nginx 配置并设置续期任务(系统里一般有 timer/cron)。

  • 模板

shell
# /etc/nginx/conf.d/example.com.conf

# 1) HTTP:只负责 acme 验证 + 跳转 HTTPS
server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    # acme 验证
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/example.com;
        default_type "text/plain";
        try_files $uri =404;
    }

    # 其余都跳转到 https
    location / {
        return 301 https://$host$request_uri;
    }
}

# 2) HTTPS:真实业务
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example.com www.example.com;

    # certbot 自动生成/维护的证书路径
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # (可选)TLS 强化
    ssl_protocols TLSv1.2 TLSv1.3;

    root /var/www/example.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}
  • 自动续期确认 + 手动测试
    • 查看 timer(snap/系统版都可能有)
shell
systemctl list-timers | grep -i certbot || true
  • 模拟续期
shell
sudo certbot renew --dry-run

Gzip 压缩 + 常用优化配置

  • 放在 http {} 里更合适:

Gzip

nginx
http {

  ##
  ## ===== Gzip 压缩配置 =====
  ##

  gzip on;                     # 开启 gzip 压缩(默认 off)
  gzip_min_length 1024;        # 响应内容大于 1KB 才压缩(太小没必要)
  gzip_comp_level 5;           # 压缩等级 1~9(5 性能/压缩率平衡,最常用)
  gzip_vary on;                # 给响应头加 Vary: Accept-Encoding
                               # 解决代理/CDN 缓存问题
  gzip_proxied any;            # 代理请求也启用 gzip
                               # any = 所有代理情况

  # 指定哪些 MIME 类型参与压缩
  gzip_types
    text/plain                 # 普通文本
    text/css                   # CSS
    application/json           # JSON 接口
    application/javascript     # JS
    text/xml
    application/xml
    application/xml+rss
    image/svg+xml;             # SVG(文本格式,适合压缩)

  # ⚠️ 不建议压缩图片(jpg/png/webp 已经是压缩格式)
  # gzip_types image/jpeg image/png; ❌

  ##
  ## ===== 传输 & 性能优化 =====
  ##

  sendfile on;                 # 内核级文件传输(静态文件性能提升)
  tcp_nopush on;               # 配合 sendfile,减少包数量
  tcp_nodelay on;              # 保证实时性(对小包/长连接友好)

  keepalive_timeout 65;        # HTTP 长连接超时时间(秒)
  keepalive_requests 1000;     # 单连接最多处理请求数

  ##
  ## ===== 上传 & 请求限制 =====
  ##

  client_max_body_size 20m;    # 单次请求最大 body(文件上传)
                               # 超过返回 413 Request Entity Too Large

  ##
  ## ===== 安全相关 Header(基础版)=====
  ##

  add_header X-Frame-Options SAMEORIGIN always;          # 防止 iframe 劫持
  add_header X-Content-Type-Options nosniff always;     # 禁止 MIME 嗅探
  add_header Referrer-Policy strict-origin-when-cross-origin always;

}

静态资源缓存

nginx
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|webp)$ {
  expires 30d;
  add_header Cache-Control "public, max-age=2592000, immutable";
}

上传大小限制

nginx
client_max_body_size 20m;

基本安全头(按需)

nginx
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;

跨域(CORS)

nginx
# CORS 允许的前端域名白名单
# key:浏览器请求的 Origin
# value:返回给浏览器的 Access-Control-Allow-Origin
map $http_origin $cors_origin {

  # 默认不允许(返回空字符串)
  default "";

  # ===== 允许的前端域名 =====
  "https://www.example.com"        $http_origin;
  "https://admin.example.com"      $http_origin;

  # 本地开发环境
  "http://localhost:3000"          $http_origin;
  "http://127.0.0.1:3000"          $http_origin;
}


location /api/ {

  ##
  ## ===== 1️⃣ 处理预检请求(OPTIONS)=====
  ## 浏览器在复杂跨域请求前自动发送
  ##

  if ($request_method = OPTIONS) {

    # 允许的跨域来源(只对白名单生效)
    add_header Access-Control-Allow-Origin $cors_origin;

    # 是否允许携带 Cookie / Authorization
    add_header Access-Control-Allow-Credentials true;

    # 允许的 HTTP 方法
    add_header Access-Control-Allow-Methods
      "GET,POST,PUT,DELETE,PATCH,OPTIONS";

    # 允许的请求头(要包含前端真实使用的)
    add_header Access-Control-Allow-Headers
      "Content-Type,Authorization,X-Requested-With";

    # 预检结果缓存时间(秒)
    # 在此时间内浏览器不会重复发 OPTIONS
    add_header Access-Control-Max-Age 86400;

    # 预检请求不需要返回内容
    return 204;
  }

  ##
  ## ===== 2️⃣ 实际请求(GET / POST / PUT ...)=====
  ##

  # 允许跨域访问的来源
  # always:即使返回 4xx / 5xx 也携带 CORS 头
  add_header Access-Control-Allow-Origin $cors_origin always;

  # 允许浏览器携带 Cookie
  add_header Access-Control-Allow-Credentials true always;

  ##
  ## ===== 3️⃣ 反向代理到后端服务 =====
  ##

  proxy_pass http://backend_pool;

  # 传递真实客户端信息
  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;
}

DANGER

❌ 错误写法(浏览器会直接拦)

nginx
Access-Control-Allow-Origin: *;
Access-Control-Allow-Credentials: true;

TIP

✅ 正确写法

nginx
Access-Control-Allow-Origin: https://www.example.com;
Access-Control-Allow-Credentials: true;

常用命令列表

shell
# 查看版本
nginx -v

# 检查配置语法(改完配置第一步)
sudo nginx -t

# 查看最终生效配置(排查 include/覆盖很有用)
sudo nginx -T

# 启动 / 停止 / 重启
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

# 平滑重载(推荐:不中断连接)
sudo systemctl reload nginx
# 或:sudo nginx -s reload

# 查看状态
sudo systemctl status nginx

# 看日志(实时)
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

模板

nginx
# 定义后端服务池(可多台)
upstream api_pool {

  least_conn;                  # 最少连接策略(更均衡)

  server 127.0.0.1:3000;       # 后端 API 服务
  # server 10.0.0.12:3000;     # 第二台(需要就打开)

  # max_fails=3 fail_timeout=10s
  # 10 秒内失败 3 次就临时踢出
}

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

  # 所有 HTTP 请求跳转到 HTTPS
  return 301 https://$host$request_uri;
}


server {
  listen 443 ssl http2;        # http2 可提升多资源加载速度
  server_name example.com www.example.com;

  ##
  ## ===== SSL 证书配置 =====
  ##

  ssl_certificate     /etc/nginx/certs/fullchain.pem;
  ssl_certificate_key /etc/nginx/certs/privkey.pem;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;

  ##
  ## ===== Gzip(站点级,也可放 http{})=====
  ##

  gzip on;
  gzip_min_length 1024;
  gzip_comp_level 5;
  gzip_types
    text/plain
    text/css
    application/json
    application/javascript
    image/svg+xml;

  ##
  ## ===== 前端静态资源 =====
  ##

  root /var/www/myapp/dist;    # 前端构建产物目录
  index index.html;

  # SPA 路由支持(Vue / React / Svelte 必备)
  location / {
    try_files $uri $uri/ /index.html;
  }

  ##
  ## ===== API 反向代理 =====
  ##

  location /api/ {

    proxy_pass http://api_pool/;   # 转发到 upstream

    # 保留客户端真实信息
    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 支持(如果不用也没坏处)
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

  ##
  ## ===== 静态资源缓存(强烈推荐)=====
  ##

  location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|webp)$ {
    expires 30d;   # 浏览器缓存 30 天

    add_header Cache-Control
      "public, max-age=2592000, immutable";
      # immutable:内容不变就不重新请求
  }

  ##
  ## ===== 基础安全 Header =====
  ##

  add_header X-Frame-Options SAMEORIGIN always;
  add_header X-Content-Type-Options nosniff always;
  add_header Referrer-Policy strict-origin-when-cross-origin always;
}