Nginx 站点配置实战

Nginx 作为网站入口,最常见的三种用法是:托管静态站点、把 PHP 请求转发给 PHP-FPM、把 API 请求反向代理给 Node.js。本文把这三份配置放在一起,并给出上线时常用的命令和容易踩的坑。无论哪种用法,配置的核心都围绕 serverlocation 两个块展开:server 决定请求匹配哪个站点,location 决定请求落到哪个处理逻辑。

场景一:静态站点 + HTTPS

静态站点(HTML、CSS、JS)配置最简单。下面的配置把 80 端口请求 301 跳到 HTTPS,并给静态资源设置一年缓存:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

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

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

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

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

    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;
}

参考:Nginx 官方文档 https://nginx.org/en/docs/

证书可以从 Let's Encrypt 免费申请,自动续期方案见Certbot 自动续期。

场景二:PHP-FPM 代理(WordPress / Laravel)

动态站点需要把 .php 请求交给 PHP-FPM 处理,try_files 的回退规则保证前端路由能正常落到 index.php

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

    root /var/www/example.com/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }
}

场景三:Node.js 反向代理

Node 应用通常监听某个本地端口(如 3000),由 Nginx 统一对外。WebSocket 场景记得带上 Upgrade 头:

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

    location / {
        proxy_pass http://127.0.0.1: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;
    }
}

关键指令说明

这几份配置里反复出现的指令值得单独说:server_name 用域名匹配请求,写错会导致 404 或匹配到默认站点;root 指定站点文件目录,必须和实际部署路径一致;try_files 按顺序尝试文件,最后一项是兜底回退;add_header 给响应追加响应头,加 always 后对所有状态码生效。反向代理场景里,proxy_pass 决定请求转给谁,proxy_set_header 负责把原始 Host、客户端 IP、协议信息透传给后端——后端日志和分析脚本都依赖这些字段。

三种配置对比

场景 核心指令 典型用途 关键注意点
静态站点 root / try_files 落地页、文档站 静态资源缓存
PHP-FPM fastcgi_pass WordPress、Laravel SCRIPT_FILENAME 别写错
反向代理 proxy_pass Node.js、API、WebSocket 转发 Host 与真实 IP

日志与访问控制

站点上线后,日志就是排障的第一手资料。默认配置已经会在 access.log 里记录每个请求,建议在 server 块里加自定义格式,把响应时间、请求体大小也记下来,方便定位慢请求:

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" $request_time';
access_log /var/log/nginx/example.com.access.log main;

同时别忘记基本访问控制:隐藏的 .git.envbackup 等目录要显式拒绝访问,管理后台可以用 allow/deny 按 IP 限制,配合 fail2ban 拦截暴力扫描。这些规则组合起来,能在配置层面先挡掉一批不速之客。另外记得用 logrotate 定期轮转日志,避免单个日志文件无限增长拖慢磁盘。

配置组织与 include

站点多了之后,把每份配置都写完整会显得冗长。Nginx 支持 include,把公共部分抽成片段:SSL 参数放 conf.d/ssl.conf,缓存规则放 conf.d/cache.conf,站点配置里用 include 引入。这样新增站点时只需要写 server_name、root 和 location 的差异部分,可维护性会好很多。

性能小贴士

静态资源缓存、开启 HTTP/2、隐藏版本号这些基础调优能让站点响应更快。HTTP/2 在 listen 443 ssl http2 里已经开启;静态资源用 expires 设置长缓存;server_tokens off 隐藏版本号,减少信息泄露。更系统的性能调优建议直接参考 Nginx 优化指南。

上线操作

# 1. 创建站点配置
sudo nano /etc/nginx/sites-available/example.com

# 2. 启用站点
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

# 3. 校验配置
sudo nginx -t

# 4. 重载 Nginx
sudo systemctl reload nginx

# 5. 查看状态
sudo systemctl status nginx

常见坑

  1. 改完配置忘记 nginx -t 与 reload。语法错误会导致整个 Nginx 拒绝启动,先 -treload 是标准流程。
  2. 代理请求里丢了真实 IP。后端拿到的 REMOTE_ADDR 全是 127.0.0.1,记得设置 X-Real-IPX-Forwarded-For
  3. SSL 证书路径写错。证书更新后路径不一致会报 no such file,建议直接用软链接管理证书路径。
  4. try_files 回退行为不符合预期。单页应用用 /index.html,PHP 应用用 /index.php?$query_string,两者别混用。
  5. proxy_pass 结尾斜杠的坑proxy_pass http://127.0.0.1:3000 不带斜杠会保留完整 URI,带斜杠则会去掉匹配到的前缀,两者行为差异不小,配置前先想清楚。

常见问题

为什么改了配置不生效? Nginx 不会自动重载,必须执行 nginx -t 校验后再 systemctl reload nginx;另外检查站点是否已软链接进 sites-enabled。www 与不带 www 的域名怎么统一? 在 80 和 443 的 server 块里都声明两个域名,并加一条 301 把 www 跳转到主域名,避免重复内容。PHP 站点返回 404 而不是执行? 常见原因是 try_files 回退写成了 /index.html,PHP 应用应该回退到 /index.php?$query_string证书快到期怎么提醒? 配合 certbot 的 renew hook,到期前 30 天自动续期,失败会发邮件通知。如何为多个站点复用配置? 把公共的缓存、安全头配置拆到 conf.d/*.conf 里 include,站点配置只保留差异部分。404 页面怎么自定义? 在 server 块里用 error_page 404 /404.html; 指定错误页,再在 root 下放一个 404.html。如何限制单 IP 请求频率?limit_req_zone 配合 limit_req,对登录接口等敏感路径做限流。

Nginx 的更多性能调优可以参考Nginx 优化指南,反向代理的完整讲解见Nginx 反向代理指南;如果要用 Docker 部署,多容器部署会更有帮助。这类配置都属于环境部署的范畴。