尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LNMP动静分离实战:从零搭建到Nginx核心配置详解

LNMP动静分离实战:从零搭建到Nginx核心配置详解 做运维和开发这些年我很清楚一件事很多人第一次接触“LNMP”都是靠一键安装包装完能看到一个 phpinfo 页面就算完成了。可真到了生产项目上同一个站点下既要跑 PHP 动态接口又要处理图片、CSS、JS 这些静态资源用户体验和服务器负载立刻原形毕露。这就是这篇文章想解决的问题。作为 Nginx 系列实战的第三篇我把 LNMP 完整搭建和动静分离从环境规划、组件安装、核心配置、多站点部署到日志运维、性能调优按我实际走过的顺序完整梳理一遍。这篇不是那种跑通即删的教程而是能直接复用到项目里的经验总结。1. 动静分离之前先把 LNMP 三者的分工理清楚很多新手一上来就写 Nginx 配置结果碰到“PHP 页面打不开”“图片 404”“CSS 样式全乱了”根本不知道问题出在哪一环。原因很简单你还没搞清楚 LNMP 里每个组件到底负责什么就去改它们之间的通信规则自然容易被配置反噬。1.1 Nginx 不是 PHP 解释器先记住这条底层认知Nginx 本质上是一个高性能的 Web 服务器和反向代理服务器它自己不具备执行 PHP 代码的能力。一个浏览器请求 .php 结尾的 URL 时Nginx 能做的只是把请求以 FastCGI 协议转发给 PHP-FPM 进程等 PHP-FPM 解析完脚本、拿到输出后再原样返回给客户端。这里得强调一个容易搞混的概念PHP 和 PHP-FPM 不是一回事。PHP 是解释器负责把 PHP 源码编译成机器可执行的指令PHP-FPM 是 PHP FastCGI Process Manager负责管理一批 PHP-CGI 进程统一接收 Nginx 转发过来的请求分配空闲进程去执行。Nginx 通过fastcgi_pass指令把请求交给 PHP-FPM两者之间要么通过 TCP 端口比如 127.0.0.1:9000通信要么通过 Unix Socket 通信。MySQL 在链路里则完全是另一个节点。PHP 脚本在处理业务时通过 PDO 或 mysqli 扩展连接 MySQL 读取数据数据库并不直接和 Nginx 打交道。所以一个请求走过的完整路径是浏览器 → Nginx → PHP-FPM → PHP 脚本 → MySQL → PHP 组装 HTML → PHP-FPM → Nginx → 浏览器。这个链路能帮你做第一层定位页面白屏先看 PHP-FPM 是否存活接口报数据库错误才需要去排查 MySQL如果是 CSS/图片打不开那大概率问题出在 Nginx 本身的静态文件处理配置上。1.2 动静分离的本质是请求分流所谓“动静分离”就是把静态文件和动态请求从一条链路里拆开。静态资源比如图片、CSS、JS、字体本质是磁盘文件Nginx 可以直接读文件返回完全不经过 PHP-FPM 和 MySQL动态请求比如用户登录、订单查询、评论提交才交给 PHP-FPM 去执行脚本、访问数据库。我见过很多项目最初的写法是把所有请求一股脑地转发给 PHP-FPM。这种做法在访问量很低时看不出什么大问题但流量一起来就很糟糕。一个 PHP-FPM 进程在处理图片时也会占用内存和 CPU甚至因为日志、Session 锁等问题被拖住导致后面真正需要处理业务的请求排队。为了更直观地理解可以看一下静态和动态两种请求的特点对比对比维度静态请求图片/CSS/JS动态请求PHP 接口/页面资源载体磁盘文件PHP 脚本 数据库处理方Nginx 直接处理PHP-FPM 执行脚本常见耗时几毫秒到几十毫秒几十毫秒到几秒并发瓶颈文件句柄和带宽PHP 进程数、MySQL 连接数缓存策略Nginx 层 expires / cache-controlRedis / opcache 等动静分离要解决的问题就是让“快的请求更快慢的请求不堵住快的请求”。这就像餐厅上菜凉菜和饮料是现成的后厨直接端上来就行热菜需要厨师现炒。如果所有客人点的菜都排队等同一个厨师那饮料也要等半天。动静分离相当于设置了两个窗口一个窗口专门出凉菜饮料一个窗口专门处理热菜各干各的效率自然上来了。2. 从零搭建环境规划、组件安装和首个 PHP 页面验证这一节开始真正动手。假设你手上有一台 Linux 服务器2 核 4G 配置系统是 Ubuntu 22.04。这个配置做中小型网站的 LNMP 测试环境完全够用实际上跑个几千 PV 的站点也没问题。2.1 版本组合与安装源的选择LNMP 对版本搭配没有特别复杂的约束但建议不要追新也不要太旧。我的这次实践用的是 Nginx 1.22、MySQL 8.0、PHP 8.1。这三个版本在 Ubuntu 22.04 官方源里都有直接 apt 安装就能得到互相兼容的版本。如果你用的是 CentOS/RHEL 系统安装方式会换成 yumPHP 官方源需要额外配置 EPEL 和 Remi 仓库。这里给一条通用经验优先使用系统包管理器的官方源不要随便去网上找一个所谓“一键编译安装脚本”。包管理器能自动处理依赖关系、日志目录、systemd 服务文件后续维护会省掉大量麻烦。有同学会遇到没有外网的生产内网环境那就需要用离线安装的方式找一台同架构、同系统版本的机器用apt-get download或yumdownloader把 nginx、php-fpm、依赖包全部下载好再拷到内网机器本地安装。这个过程最麻烦的是依赖树整理建议生成下载清单后逐项检查不要漏掉。2.2 安装 Nginx、MySQL、PHP-FPM 的关键步骤更新源后一次性安装所有组件apt update apt install -y nginx mysql-server php8.1-fpm php8.1-cli php8.1-mysql很多人装完 PHP 只会安装 php-fpm 和 php-cli结果发现连不上 MySQL其实是因为少装了php8.1-mysql这个扩展。PHP 连接数据库走的是扩展而不是 PHP 核心自带的这一点要牢记。MySQL 安装完成后建议立刻执行安全初始化命令mysql_secure_installation这个操作会引导你设置 root 密码、删除匿名用户、禁止 root 远程登录、删除测试数据库。很多没做这步的服务器后期很容易被扫描到弱口令和未授权访问。如果安装时提示nginx: 未找到命令但包管理器又显示已安装大概率是执行用户 PATH 环境变量里没有/usr/sbin目录。直接用whereis nginx找到绝对路径或者用/usr/sbin/nginx执行即可。2.3 服务启动、端口确认和开机自启安装完成后依次启动三个服务并设置开机自启systemctl enable --now nginx php8.1-fpm mysql然后检查监听端口ss -lntp | grep -E :(80|9000|3306)正常情况下应该能看到Nginx 监听 80 端口MySQL 监听 3306PHP-FPM 默认监听 Unix Socket。这里特别说一下 PHP-FPM 的监听方式因为它直接决定了后面 Nginx 配置里的fastcgi_pass怎么写。PHP-FPM 默认配置通常监听 Unix Socket路径类似/run/php/php8.1-fpm.sock。Unix Socket 不走网络协议栈性能比 TCP 略好适合 Nginx 和 PHP-FPM 在同一台机器上的场景。另一种是 TCP 方式PHP-FPM 监听 127.0.0.1:9000适合 Nginx 和 PHP 分离在不同机器上的架构。如果你用了listen 127.0.0.1:9000那 Nginx 里的fastcgi_pass必须也是127.0.0.1:9000两边不一致会导致 502 Bad Gateway。2.4 最小化配置让 PHP 页面跑起来在配置动静分离之前先用最精简的 Nginx server 块让 PHP-FPM 跑通排除后面排查时的干扰项。先创建站点目录mkdir -p /var/www/blog cat /var/www/blog/index.php EOF ?php phpinfo(); EOF然后修改 Nginx 默认站点配置或者新建一个站点配置文件server { listen 80; server_name blog.example.com; root /var/www/blog; index index.php index.html; location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; } }这段配置里最关键的是include snippets/fastcgi-php.conf它会把 FastCGI 请求需要的一组默认参数带进来其中包含SCRIPT_FILENAME等关键参数。如果没有这一行你可能会遇到“PHP 文件被下载而不是执行”的问题——浏览器打开一个 .php 地址屏幕上显示的是源码这就是 Nginx 把 PHP 文件当成普通文件直接返回了。保存配置后先用nginx -t校验语法再systemctl reload nginx最后用 curl 验证curl -I http://127.0.0.1/能看到HTTP/1.1 200 OK并且页面内容是 PHP 渲染出的 HTML说明 Nginx 和 PHP-FPM 已经打通了。3. 动静分离配置核心location 优先级、缓存与 fastcgi 参数PHP 页面跑通只是完成了第一阶段接下来才是这篇文章的重头戏动静分离。而动静分离配置的核心就是 Nginx 的location匹配机制。3.1 location 匹配规则决定你的请求到底去哪Nginx 的location指令按匹配优先级分为几档很多人只记得“正则匹配优先于普通前缀”这个理解其实不完整。完整的优先级从高到低是这样的匹配方式写法示例优先级精确匹配location /logo.png最高前缀匹配且不再检查正则location ^~ /static/高正则匹配按配置文件顺序location ~* \.(js|css)$中普通前缀匹配取最长匹配location /static/低默认匹配location /最低这段规则在配置中是有实际后果的。如果我把location ~* \.(png|jpg|css|js)$写在前面而location ^~ /static/写在后面图片请求依然会命中正则规则因为正则的优先级高于普通前缀。但如果不希望正则去匹配 /static/ 目录下的请求就必须用^~表示法告知 Nginx 一旦命中该前缀就不要再继续查找正则规则了。3.2 root 和 alias静态资源 404 的头号原因讲静态资源配置逃不开root和alias的区分。这是我以为自己懂了、结果实际踩坑时最多次的一个点。root指令会把完整 URI 拼接到指定目录后面。比如location /static/ { root /var/www/blog; }请求/static/a.png实际去磁盘找的是/var/www/blog/static/a.png。而alias指令会用 alias 指定的路径替换掉 location 匹配到的部分location /static/ { alias /var/www/blog/static/; }请求/static/a.png实际去找的是/var/www/blog/static/a.png。如果这里误写为root /var/www/blog/static/那 Nginx 会去/var/www/blog/static/static/a.png找文件返回 404。写静态资源 location 时一个常用配置是location ^~ /static/ { alias /var/www/blog/static/; expires 30d; add_header Cache-Control public, immutable; }这样 Nginx 直接从磁盘返回静态文件并且告诉浏览器这些文件 30 天内可以强缓存后续用户刷新页面时浏览器都不会再发起请求。3.3 静态缓存和防盗链的补充配置文件缓存配置里expires 30d是一个比较粗的粒度。我的习惯是带 hash 指纹的文件名比如 app.8f3k2a.js用immutable缓存一年普通图片用Cache-Control: public, max-age86400缓存一天HTML 页面不要设长缓存否则改版后用户会看到旧页面。缓存策略要根据静态资源的更新频率来定不可能一套配置吃遍所有场景。如果图片资源比较宝贵还可以加一个简单的防盗链location ~* \.(gif|jpg|png|webp|svg)$ { valid_referers none blocked *.example.com example.com; if ($invalid_referer) { return 403; } }valid_referers里的none表示允许直接通过地址栏访问blocked表示允许没有带 Referer 的请求*.example.com则限定只有这些域名来的请求才能使用图片。对个人站点来说这个配置能挡住大部分简单盗链但也别指望它能防御任何技术攻击这点认知要有。3.4 fastcgi 参数PHP 请求不出幺蛾子的底线动态请求的 location 写法如下location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }这里重点说两个细节。第一个是try_files $uri 404;。这个指令在 fastcgi-php.conf 里通常已经被引入它的作用是如果请求的 PHP 文件在磁盘上不存在就直接返回 404不把请求传给 PHP-FPM。没有它一个不存在的 .php 路径也可能被 PHP-FPM 接收执行产生安全问题。第二个是SCRIPT_FILENAME。它的值是实际执行脚本的绝对路径由$document_root$fastcgi_script_name组合而成。如果文档根目录配置错了或者 root 和 alias 混用后 document_root 不符合预期这里就会出现“PHP 文件找不到”的 404或者在日志里看到Primary script unknown错误。排这类问题第一件事永远是去 error.log 看 fastcgi 相关报错别在配置文件里瞎猜。4. 多项目部署与反向代理一台 Nginx 的多种挂载姿势完成了单站点动静分离之后现实里更常遇到的问题是如何在一台 Nginx 上部署多个 Web 项目。有些是多个域名有些是同一个域名下的不同路径有些项目甚至不是 PHP 写的而是跑在另一个端口上的服务这时候 Nginx 的角色就从 Web 服务器变成了反向代理。4.1 用 server_name 规划多站点最推荐的多项目方式是一个项目一个独立域名在 Nginx 里就是多个server块。每个server块独立监听、独立配置 root、独立定义日志路径互不干扰。server { listen 80; server_name blog.example.com; root /var/www/blog; access_log /var/log/nginx/blog.access.log; } server { listen 80; server_name shop.example.com; root /var/www/shop; access_log /var/log/nginx/shop.access.log; }这种方式的优点是配置隔离清晰一个站点挂了不影响另一个。缺点是需要有多个域名而且每个域名要正确解析到这台服务器上。如果没有域名也可以用端口区分server { listen 8080; server_name localhost; root /var/www/project-b; }端口区分适合内部测试但对于线上生产不建议常态化使用因为非标准端口在防火墙策略、安全扫描上总会多出不少麻烦事。4.2 子路径挂载项目时容易踩的坑当项目用同一个域名的不同子路径区分时配置的复杂度会明显上升比如业务要求既是www.example.com又是www.example.com/project-b。第一层是 location 前缀匹配server { listen 80; server_name www.example.com; root /var/www/main; location / { try_files $uri $uri/ /index.php?$query_string; } location ^~ /project-b/ { alias /var/www/project-b/; index index.php; location ~ \.php$ { fastcgi_pass unix:/run/php/php8.1-fpm.sock; } } }这里最容易踩的坑是子项目里的 PHP 文件和静态文件路径全乱套。原因还是 root 和 alias 的区别。在location ^~ /project-b/内部PHP 请求的SCRIPT_FILENAME必须对应 alias 后的真实路径否则 PHP-FPM 接收到的脚本路径不正确就会 404。第二层是前端层面的问题如果项目 B 是一个 Vue 或 React 应用打包后引用的资源路径是/assets/app.js那么它加载资源时会跳到根路径/assets/去找而不是/project-b/assets/。解决方案是要在打包工具里配置 base 路径Vue 项目在vite.config.js里设置base: /project-b/这才算真正把这个项目挂在了子路径下。4.3 反向代理场景与 IP 五元组的实际影响说到 Nginx 反向代理就得解决一个新手经常问的问题Nginx 转发请求后后端服务收到的客户端 IP 到底是不是真实 IP先明确 TCP/IP 层面的五元组概念源 IP、源端口、目标 IP、目标端口、协议。假设客户端 A 访问 Nginx假设 Nginx 和业务服务在同一台机器Nginx 作为代理会新建一条到后端的 TCP 连接后端看到的连接源 IP 是 127.0.0.1源端口是一个随机端口目标 IP 是 127.0.0.1目标端口是业务服务端口协议是 TCP。也就是说纯粹站在 TCP 五元组的角度后端永远看不到客户端 A 的真实 IP。要让后端获取真实客户端 IP需要在 Nginx 这一层把信息通过 HTTP 头传递过去location /api/ { proxy_pass http://127.0.0.1:8081/; 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; }X-Real-IP只带一层的真实 IPX-Forwarded-For则是把整个代理链上的 IP 都记录下来多个值之间用逗号分隔。后端应用如果要取 IP优先取X-Forwarded-For里最左边那个值才是最初客户端的地址。如果有多层代理也要注意不要直接信任这个头否则客户端也能伪造这就涉及到可信代理白名单的问题了。4.4 静态资源走 Nginx动态接口走反向代理动静分离的另一种形态动静分离不只针对 PHP。很多现代项目的架构是 Nginx 后端 API 服务后端用 Java、Go 或 Node 写的监听在 8081 端口或通过 Unix Socket 通信。这时 Nginx 配置通常写成server { listen 80; server_name api.example.com; location / { proxy_pass http://127.0.0.1:8081; proxy_http_version 1.1; proxy_set_header Connection ; } }这种场景下静态资源如果也放在同一台机器上就需要单独设计一套静态文件目录或 CDN 策略而不是继续让 API 服务去读文件。上个月我部署一个前后端分离项目时就是让 Nginx 直接管理/var/www/fe目录的静态产物接口请求才反向代理到 8081 端口整体性能和部署复杂度都比原来直接用 Node 托管静态文件好得多。5. 日志、状态页与检查工具排查问题时最愿意看到的东西配置写的再花哨上线后真正考验你的是定位问题的速度。日志是这一节的主角不管什么疑难杂症第一条排查路径基本都是打开日志看报错。5.1 access_log 和 error_log 的路径与格式Nginx 的日志分访问日志和错误日志两条线。访问日志记录每个请求的基本信息错误日志记录 Nginx 处理过程中的异常。默认路径一般在/var/log/nginx/access.log /var/log/nginx/error.log访问日志默认把所有字段混在一行里可读性一般。建议自定义日志格式方便后续归档和分析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_x_forwarded_for因为经过代理后$remote_addr很可能都是 Nginx 本机地址只有通过 X-Forwarded-For 才能看到真实客户端来源。错误日志的级别一般用warn就够了。error只记录严重错误notice和info信息量太大容易把真正的问题刷过去。调试阶段可以临时改成debug但上线前必须改回来否则磁盘会被日志撑爆。error_log /var/log/nginx/error.log warn;5.2 开启 stub_status 状态页Nginx 自带的stub_status模块可以直接输出当前连接状态是一个轻量又实用的监控入口location /nginx_status { stub_status on; access_log off; allow 10.0.0.0/8; deny all; }访问后你会看到类似这样的输出Active connections: 12 server accepts handled requests 10245 10245 20831 Reading: 0 Writing: 1 Waiting: 11Active connections是当前活跃连接数accepts是从启动以来接受的连接总数handled是处理的握手总数正常情况下这两个数应该相等如果相差过大说明有连接被丢弃很可能是文件句柄不够requests是总请求数Reading、Writing、Waiting分别表示正在读请求头、正在写响应、等待请求的空闲连接数。这台状态页不要暴露到公网用allow白名单限制住是最基本的底线。5.3 可视化配置工具与 nginx -t每次改动 Nginx 配置后第一件事一定是先执行nginx -t如果配置里有语法错误它会直接告诉你错在哪个文件哪一行。没有报错再 reloadsystemctl reload nginx关于可视化配置工具网上确实有一些比如部分 Nginx 管理面板可以点一点生成 server 块、查看状态。我个人的态度是可以作为辅助查看流量和状态但不要完全依赖它来管理线上配置。原因很简单这类工具生成的配置往往带有自己的模板和格式一旦它生成的配置和你手工维护的配置风格不一致后续合并、排查反而更麻烦。真正稳妥的做法是自己掌握配置文件的基本语法把域名、证书、日志路径、location 规则这些基础内容管理好。5.4 用命令行快速分析访问日志排查问题时awk 和 grep 是一对很好用的工具。比如查看访问日志里响应状态码的分布awk {print $9} /var/log/nginx/access.log | sort | uniq -c | sort -rn看今天累计访问量前 10 的 URLawk {print $7} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10看最近一分钟有没有 500 报错tail -1000 /var/log/nginx/access.log | awk $9500 {print}这些命令不用记太复杂但关键时刻能帮你快速缩小问题范围。日志能告诉你的不只是有没有问题还能告诉你问题集中在哪个 URL、哪个 IP、哪个时间段这些信息在排查现场非常救命。6. 上线后的参数调优与高频踩坑复盘LNMP 跑起来之后还有一个绕不开的阶段调优。调优这件事不能盲目照抄网上的所谓“最佳配置”而是要通过压测和观察逐步修正。我在这里整理几个通用的参数基线以及我踩过、也帮别人解决的问题清单。6.1 Nginx 核心参数基线worker_processes auto; worker_rlimit_nofile 65535; events { worker_connections 4096; use epoll; } http { sendfile on; tcp_nopush on; keepalive_timeout 30; keepalive_requests 1000; gzip on; gzip_min_length 1k; gzip_types text/plain text/css application/javascript application/json image/svgxml; client_max_body_size 20m; }worker_processes auto会让 Nginx 按 CPU 核心数启动 worker 进程一般不用手动改。worker_connections是每个 worker 能同时处理的连接数上限。一个经验公式是最大并发连接数 ≈ worker_processes × worker_connections。如果你的机器是 2 核理论上这个配置能支撑 8000 左右的并发连接但注意这是一个理论值实际还要受内存、带宽、PHP-FPM 处理能力等因素限制。sendfile on是让 Nginx 直接通过内核态发送文件减少数据从磁盘到用户态的拷贝tcp_nopush配合sendfile可以减少网络包的数量keepalive_requests 1000表示一个长连接最多可以复用 1000 次请求而不是每次请求都重新建 TCP 连接这点对 TLS 场景尤其重要。6.2 PHP-FPM 侧容易被忽视的两个设置PHP-FPM 的默认配置在www.conf里几个关键参数是pm dynamic pm.max_children 50 pm.start_servers 10 pm.min_spare_servers 5 pm.max_spare_servers 20 pm.max_requests 1000pm.max_children决定 PHP-FPM 最多能启动多少个 worker 进程。不要盲目调大因为每个 worker 占用的内存大约是 20~50MB如果机器 4G 内存max_children 设到 200 直接就会 OOM。一个比较稳的计算方式是可分配内存 / 单个 PHP 进程平均内存先设一个小值配合监控数据逐步调大。pm.max_requests建议必开。它的作用是让 PHP-FPM 在 worker 处理超过一定数量请求后自动重启该进程防止 PHP 脚本里的少量内存泄漏无限累积。这个参数能帮你省下很多半夜被内存告警吵醒的时间。另外强烈建议开启慢日志定位那些写得比较慢的 SQL 或接口slowlog /var/log/php8.1-fpm-slow.log request_slowlog_timeout 5s当某个 PHP 请求执行超过 5 秒就会被记录到慢日志里并直接打印出调用堆栈。这是排查“页面偶尔卡住”问题的利器。6.3 高频问题复盘清单现象根本原因解决方案访问 .php 显示文件源码Nginx 没把 PHP 交给 FPM加上include snippets/fastcgi-php.conf和fastcgi_pass502 Bad GatewayNginx 连不上 PHP-FPM检查 socket 路径或 9000 端口是否匹配、FPM 进程是否存活静态资源 404root 和 alias 混用按 3.2 的逻辑重新设计 location改完配置不生效没有 reload 或nginx -t没通过每次改配置先 test 再 reload页面响应慢可能是 PHP-FPM 进程数不足看慢日志和 php-fpm 状态页调整 max_children日志里全是客户端真实 IP 丢失代理层没传 XFF 头配置 4.3 的 proxy_set_header安全扫描报告旧版 Nginx 漏洞版本过旧不要无视扫描结果及时升级到修复版本关于最后一条多说一句漏洞扫描报出 Nginx 的 CVE 编号时第一反应不是在网上找各种奇奇怪怪的补丁脚本而是去官方源里升级版本。多数情况一条apt upgrade nginx就能解决前提是你当时安装时用了官方源而不是来路不明的编译包。Windows 下部署 Nginx 的同学也要注意nginx.exe直接双击启动虽然方便但关闭窗口时 Nginx 进程并不会自动退出。生产环境在 Windows 上运行最好注册成 Windows 服务或者用start /b nginx.exe的方式后台启动否则很容易出现“我把窗口关了服务怎么还在占着 80 端口”的尴尬。7. 生产级配置参考与一次真实验证最后给一个我实际用过的 LNMP 动静分离完整配置模板你可以直接另存后按域名和目录替换使用。7.1 完整参考配置server { listen 80; server_name www.example.com; root /var/www/example; index index.php index.html; access_log /var/log/nginx/example.access.log main; error_log /var/log/nginx/example.error.log warn; # 静态资源交给 Nginx 直接返回设置缓存 location ^~ /static/ { alias /var/www/example/static/; expires 30d; add_header Cache-Control public, immutable; } # 图片走正则匹配加防盗链 location ~* \.(gif|jpg|jpeg|png|webp|svg|ico)$ { expires 7d; valid_referers none blocked *.example.com example.com; if ($invalid_referer) { return 403; } } # 动态 PHP 请求交给 PHP-FPM location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } # 默认规则 location / { try_files $uri $uri/ /index.php?$query_string; } }这份配置的关键思路静态目录用^~直接锁定不让正则干扰图片正则匹配后加防盗链PHP 请求通过精确的.php$正则进入 FastCGI默认 location 兜底所有未匹配的请求转发到入口文件。7.2 验证动静分离是否生效配置完成后用 curl 看响应头curl -I https://www.example.com/static/logo.png curl -I https://www.example.com/list.php静态资源响应头里能看到Cache-Control: public, max-age2592000说明缓存生效PHP 页面响应头里可以看到X-Powered-By: PHP/8.1之类标记说明请求确实走过了 PHP-FPM。再用nginx -t最终校验一下配置文件然后systemctl reload nginx完成。如果条件允许建议用压测工具模拟一下 200 个并发观察静态资源和动态接口的响应时间差异。动静分离生效后两类请求的表现会有非常明显的区分度静态资源占用非常稳定动态接口则更依赖 PHP-FPM 和数据库的支撑能力。7.3 一点实践体会做 Nginx 配置这些年我最深的感受是不要为了“看起来高级”而做动静分离也不要今天听人说反代好明天就把所有流量都导过去。每一个配置决策都应该能讲清楚它解决的是什么问题。动静分离解决的是静态资源占用 PHP 进程的问题缓存解决的是重复计算的问题反向代理解决的是服务拆分后统一入口的问题——问题对了方案自然就对了。这次完整走了一遍 LNMP 搭建和动静分离的配置我把整个过程中最小可复用的经验都提炼到了上面。如果你正在部署自己的建站环境照着这份思路搭一遍遇到问题时按日志和 location 匹配规则去排查基本能把绝大多数日常问题控制在半小时内解决。下一期我应该会聊聊 Nginx 的缓存策略和 HTTPS 配置这两块也是实战里绕不开的硬骨头。
返回列表