1. 部署前的准备与安装选型
1.1 静态站点部署的核心思路
先说说静态HTML到底是什么。简单讲,就是浏览器拿到什么就渲染什么,不需要后端程序动态生成页面内容,典型的就是个人博客、产品介绍页、前端打包后的dist目录、或者一个纯展示型的官网。这类站点的最大特点是:文件就是一切,服务器要做的只是把磁盘上的html、css、js、图片原样发给浏览器。
那为什么选Nginx而不是Apache,也不是直接用Python的http.server或者Node的serve?我个人的看法是,Nginx在高并发下表现极其稳定,内存占用低,配置语法清晰,而且静态文件处理能力几乎是目前主流Web服务器里最强的那一档。Apache当然也能做,但配置语法偏老派,性能上在同配置下也不占优。Node的serve这类工具更适合本地开发调试,放到生产环境里无论是稳定性还是安全性都不够看。所以生产环境静态站点,Nginx基本是默认答案,也是绝大多数云厂商镜像里自带的标配。
另外要提的是,很多人以为静态站点部署就是“把文件丢进服务器就能访问”,其实中间还隔着三个环节:文件放到哪个目录、Nginx怎么知道去这个目录找文件、访问者的请求怎么到达Nginx。这三个环节对应到Nginx配置里,就是root指令、server块监听端口、以及系统防火墙/安全组放行规则。这篇文章的核心目标,就是把这条链路彻底打通。
1.2 Nginx安装的几种方式对比
Nginx的安装方式主要有三种,我按个人推荐程度排序说明。
第一种是包管理器安装。Ubuntu/Debian系执行apt install nginx,CentOS/RHEL系执行yum install nginx(新版用dnf)。这种方式的优势是快、省心,安装完系统自动注册好systemd服务,开机自启一条命令搞定,后续卸载也干净。缺点是版本往往偏旧,拿Ubuntu 20.04来说,自带的Nginx版本是1.18,虽然稳定,但一些新特性用不上。如果你对版本没有执念,就选这个。
第二种是源码编译安装。去nginx.org下载源码包,自己configure、make、make install。这种方式的好处是版本最新、可以自定义编译模块(比如需要某些第三方模块时只能走编译路线),安装路径也完全可控。坏处是需要装一堆编译依赖(gcc、pcre-devel、zlib-devel、openssl-devel等),编译过程要花几分钟,后续卸载全靠手动。除非你明确需要特定模块或最新版本,否则我没必要让小白一上来就走这条路。
第三种是用Docker跑容器。docker run一个nginx镜像,通过-v挂载静态目录和配置文件。这种方式和宿主机环境完全隔离,环境干净、迁移方便,适合本身已经容器化的业务。缺点是排障链路多一层,文件权限映射偶尔也会踩坑。如果你是纯静态站点、服务器上也没跑别的容器,直接用宿主机安装反倒更简单直接。
我接下来以Debian/Ubuntu系系统的apt安装为主线讲,因为这是最通用、最少坑的路径,源码编译部分会单独开一节说明编译参数。
1.3 验证安装与基础目录结构
安装完成后,先别急着改配置,花两分钟摸清Nginx的文件布局。不同安装方式的目录略有差异,apt安装的默认结构大概是这样的:
- /etc/nginx/:主配置目录,nginx.conf在这里
- /etc/nginx/sites-available/:站点可用配置目录
- /etc/nginx/sites-enabled/:站点启用配置目录
- /etc/nginx/conf.d/:额外的配置文件目录
- /var/www/html/:Ubuntu下默认的网站根目录
- /var/log/nginx/:access.log和error.log都在这里
执行nginx -v可以看版本号,执行systemctl status nginx看服务状态。如果一切正常,直接访问服务器IP,就能看到Nginx默认的欢迎页。这一步很关键,它证明了Web服务本身已经跑起来了,后面配置出问题,你可以确定问题出在配置或文件路径上,而不是进程没起来。
注意:如果你是刚买的云服务器,访问IP没看到欢迎页,先检查云控制台的安全组是否放行了80端口,再检查服务器内部防火墙,顺序不要反。很多新手在这上面卡了一晚上,其实和安全组规则有关。
2. Nginx静态站点核心配置详解
2.1 配置文件整体结构与加载顺序
Nginx的配置看起来很长,但只要抓住一条主线就能看懂:Nginx启动时读主配置文件nginx.conf,里面通过http块包含server块,每个server块处理一类请求。apt安装的nginx.conf末尾通常有include /etc/nginx/sites-enabled/*,意思就是把sites-enabled目录下所有文件的内容“粘贴”进来。所以你在sites-enabled里放多少个server块,Nginx就能处理多少个站点。
这个设计的精髓在于分离。nginx.conf管全局公共配置(运行用户、进程数、日志路径),而每个具体站点的配置独立成一个文件,互不干扰。新增一个站点,只需要在sites-available里新建配置文件,然后做一个软链接到sites-enabled,这个操作习惯很多人不理解,为什么不能直接放sites-enabled?原因很简单:sites-available相当于“仓库”,不参与加载;sites-enabled才是“上架”的配置。临时下线一个站点,删掉软链接就行,原文件还在仓库里,随时可以恢复。
验证配置语法用nginx -t,它会提示配置文件是否有语法错误。这个命令每次改完配置都必须执行,我见过太多人改完直接reload,结果语法错误导致Nginx拒绝重启,线上站点全线挂掉。
2.2 server块三个核心指令:listen、root、index
一个最基本、只服务单个静态站点的server块长这样:
server { listen 80; server_name example.com www.example.com; root /var/www/example; index index.html; location / { try_files $uri $uri/ =404; } }逐个拆解。listen指定监听端口,80是HTTP默认端口,如果你有多个server块,Nginx会根据请求的Host头(也就是域名)来匹配对应server块,匹配不到就找默认server块。server_name就是域名匹配的规则,支持精确匹配、通配符和正则,静态站点场景下填你的域名或IP即可。
root是整个站点文件的根目录,Nginx收到请求后会把URL路径拼在root后面去磁盘上找文件。举个例子,root是/var/www/example,请求/,它找/var/www/example/;请求/about.html,它找/var/www/example/about.html。index指定的是当请求路径指向目录时,默认返回哪个文件,比如请求/目录,它就尝试返回index.html或index.htm。
location /这个块是核心中的核心。location后面跟的是URL路径前缀,这里的/表示匹配所有请求。里面那行try_files $uri $uri/ =404,意思是:先尝试请求的完整路径是否有对应文件,没有就尝试把它当目录找默认文件,再找不到就返回404。这行配置防止了访问不存在文件时出现意外的目录索引或500错误。
2.3 location匹配规则与alias的差异
location在静态站点里主要干三件事:精确匹配某个路径、做目录别名、控制缓存和静态文件处理。它支持几种匹配优先级,从高到低是:=精确匹配、^~前缀匹配、正则匹配(~和~*)、普通前缀匹配。
很多人在配置时有个误区:以为location /和location /static/可以同时生效并叠加。实际上Nginx只选一个location来处理请求,规则是按优先级匹配,不是拼接。比如你配置了location /static/的root和location /的root,请求/static/a.js只会匹配到/static/那个location,不会先匹配/再去匹配/static/。
再说alias。root和alias都能改文件查找路径,但行为完全不同。root是“拼接”,alias是“替换”。比如:
location /static/ { alias /data/files/; }请求/static/logo.png,Nginx会去找/data/files/logo.png,注意是直接替换掉/static/这个前缀。如果这里误写成root /data/files/,它就会去找/data/files/static/logo.png,路径直接就错了。这是静态站点配置里非常经典的一个坑,值得单独拉出来强调。
2.4 多站点部署场景下的配置组织
部署多个web项目是Nginx的拿手好戏,而且方式不只一种。如果多个站点共用同一个IP,域名不同,那就每个域名一个server块,server_name区分,这是最标准的做法。我举个例子,A站点是blog.example.com,B站点是shop.example.com,两个server块监听同一个80端口,Nginx通过Host头分发。
如果只有一个域名,但想通过路径区分项目,比如example.com/和example.com/app/指向两个不同目录,那就用location前缀加alias实现。前面已经说了root和alias的区别,这里正好用上。如果端口充足,也可以用不同端口区分项目,比如8010、8020各挂一个项目,但这样URL里会带端口,体验上不太好看,而且如果涉及HTTPS证书,端口的方案会让证书配置复杂很多。
多站点下最需要留意的坑是默认server块。Nginx允许一个IP上配置多个server块,但请求的Host头不匹配任何server_name时,它会走“默认server块”——要么是配置文件里的default_server,要么是第一个加载的server块。这意味着你新加的站点配置没生效时,很可能是因为请求被旧的默认站点接管了。稳妥的做法是,在监听80端口时单独指定一个default_server。
3. 完整实操:从html文件到线上访问
3.1 准备站点文件与目录结构
我拿一个实际场景来走全程:现在有个前端项目,构建产物是一个dist目录,里面是index.html、assets文件夹等静态资源。要把它部署到一台全新的Ubuntu服务器上。
第一步,把文件传到服务器。我习惯用scp或者rsync,rsync在大量文件时优势明显,支持断点续传和增量同步。命令大概是rsync -avz dist/ root@your-server:/var/www/mysite/。如果没有本地文件只想测试,也可以直接在服务器上建一个测试页:
mkdir -p /var/www/mysite echo '<h1>Hello Nginx</h1>' > /var/www/mysite/index.html这里有个目录设计的建议:/var/www/下按项目名建子目录,每个项目一个文件夹,别把所有文件堆在/var/www/html里。一是目录清晰,多个项目好区分;二是每个站点的配置里root指向明确,不会互相污染。
3.2 编写站点配置并测试
在sites-available下新建配置文件,名字我用域名或项目名命名,方便识别:
vim /etc/nginx/sites-available/mysite写入配置:
server { listen 80; server_name your-domain.com; root /var/www/mysite; index index.html; location / { try_files $uri $uri/ =404; } }保存后,执行nginx -t检查语法。看到syntax is ok和test is successful两行提示,就说明配置没问题。接着创建软链接到sites-enabled:
ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/然后重载Nginx:
systemctl reload nginx注意我用的是reload不是restart。reload是平滑重载,Nginx会重新读取配置文件但不会中断当前连接,对线上服务友好得多;restart是强杀重启,会造成瞬间断连。日常配置变更,一律用reload。
3.3 设置开机自启
如果你用的是apt安装,Nginx服务已经注册了systemd,直接执行:
systemctl enable nginx systemctl start nginx第一条命令是设置开机自启,第二条是立即启动。如果没有systemd(比如某些精简容器环境),老派做法是把nginx命令写进/etc/rc.local,或者写一个init脚本,但现在主流的Linux发行版基本都支持systemd,这一步通常是最省心的。
验证自启是否生效,可以执行systemctl is-enabled nginx,返回enabled就代表当前已在开机启动列表里。
3.4 部署多个web项目的实战配置
在同一个IP上部署两个静态站点,域名分别是site-a.com和site-b.com,配置文件可以这样组织:
server { listen 80; server_name site-a.com; root /var/www/site-a; index index.html; } server { listen 80; server_name site-b.com; root /var/www/site-b; index index.html; }两个server块放在同一个文件或者两个文件都行。我的习惯是一个站点一个文件,文件名和域名对应,这样改配置时不需要翻找。
再补充一个前端单页应用(SPA)常遇到的情况:路由用的history模式,刷新子路径时出现404。原因很简单:浏览器请求/site-a/about,Nginx去/var/www/site-a/找about文件,找不到返回404。解法是把try_files改成兜底到index.html:
location / { try_files $uri $uri/ /index.html; }这样所有找不到文件的请求都会回退到index.html,前端路由自己去解析路径。这个坑在部署Vue和React项目时几乎必踩,记住了能省不少排查时间。
4. 常见问题排查与避坑指南
4.1 403 Forbidden的三种常见原因
403是静态站点最常见的错误,我遇到过的情况基本可以归为三类。
第一类是文件权限不足。Nginx的worker进程运行在www-data用户下(apt安装默认),如果你的站点目录权限是700,所有者是root,那www-data用户就完全没有读取权限。解决方式是设置合适的目录权限,我常用的策略是:目录755、文件644,所有者可以是root,因为www-data只要能读取就行。执行:
chown -R root:root /var/www/mysite chmod -R 755 /var/www/mysite第二类是index.html缺失。root指向的目录里没有配置里指定的index文件,Nginx会拒绝列出目录内容(出于安全考虑,默认关闭autoindex),于是返回403。检查一下目录里到底有没有index.html,或者把index指令改成实际存在的文件名。
第三类是SELinux拦截。这个在CentOS/RHEL系上概率很大,Ubuntu默认不开SELinux所以没这问题。如果是CentOS,检查一下SELinux状态,临时关闭可以执行setenforce 0,但正确做法是给网站目录打上正确的上下文标签:
chcon -R -t httpd_sys_content_t /var/www/mysite4.2 404 Not Found的排查路径
404说明请求已经到达Nginx,但文件没找到。先确认URL路径和文件实际路径的对应关系,这里最容易犯的错就是root和alias的误用,前面已经着重讲过。其次确认文件是否真的存在,最简单的方式是直接curl访问和本地cat验证,排查时我最常执行的命令组合是:
curl -I http://your-domain.com/path ls -l /var/www/mysite/path一个在真实项目中遇到过的case:前端打包后,资源文件的路径是/xxx/assets/index.js,但实际目录里assets在项目的下一级,结果所有js、css都404。把root指到正确的一层目录后瞬间恢复。这种问题光看配置不一定看得出问题,一定要结合浏览器开发者工具里“Network”标签页,看看具体哪些文件404了,路径结构一目了然。
4.3 端口占用与被忽略的监听冲突
80端口被占用的典型报错是nginx: [emerg] bind() to 0.0.0.0:80 failed。最常见的原因是Apache或者其他Web服务先占用了端口。排查命令:
ss -lntp | grep :80看到占用进程后,要么停掉旧服务,要么改Nginx的监听端口。还有一种是重复监听:nginx.conf里本身配置了listen 80 default_server,sites-enabled里又配置了一个listen 80,虽然Nginx会启动但可能行为不符合预期,排查时也要检查一下是不是有多个文件都对同一个端口做了监听配置。
4.4 修改配置后不生效的真相
这个问题出现频率极高。现象是:改了配置文件,reload也执行了,但访问结果还是旧样子。原因绝大多数是浏览器缓存。静态文件如果配置了强缓存,浏览器在缓存过期前根本不会去服务器请求。排查时开浏览器无痕窗口,或者用curl直接请求看响应头,比如:
curl -I http://your-domain.com/index.html看响应头里的Cache-Control和Last-Modified字段。如果缓存时间很长,而你又确定了Nginx配置没问题,那就是缓存策略需要调整。在静态站点场景下,合理的做法是对html文件禁止缓存或设置短缓存,对带hash的静态资源(css、js)设置长缓存。
4.5 实测排查速查表
我把静态站点部署中常见的问题整理成一张速查表,方便你遇到问题时快速定位:
| 现象 | 排查顺序 | 常用命令 |
|---|---|---|
| 输入IP无法访问 | 安全组 → 防火墙 → Nginx状态 | systemctl status nginx / ss -lntp |
| 能访问但全部404 | 文件路径 → root配置 → index配置 | ls -l /var/www/... / nginx -T |
| 403 Forbidden | 文件权限 → SELinux → index是否存在 | ls -la /var/www/... / getenforce |
| 页面样式丢失 | 静态资源路径 → alias配置 → 构建产物路径 | 浏览器Network面板 / curl -I |
| 某路径反复跳首页 | try_files回退规则 → 前端路由配置 | cat nginx站点配置 |
| reload报错 | 配置语法 → 监听端口冲突 | nginx -t / ss -lntp |
5. 性能优化与进阶扩展
5.1 静态资源的高效传输:gzip压缩与缓存策略
静态站点优化,能不引入额外复杂度就做到效果的最大提升点,一个是压缩,一个是缓存。
gzip压缩的原理是在服务端把文本文件压缩后再传给浏览器,浏览器解压后渲染。现在的浏览器基本都支持gzip,Nginx配置起来也简单:
gzip on; gzip_types text/plain text/css application/javascript application/json image/svg+xml; gzip_min_length 1024;gzip_min_length 1024的意思是小于1KB的文件不压缩,因为压缩本身有CPU开销,小文件压缩后体积反而可能变大。图片本身已经压缩过,通常不放进gzip_types,jpg、png这类格式对压缩不敏感,强行gzip只会浪费CPU。
缓存策略建议按文件类型区分。带hash的文件名(比如app.8f3k2j.js)内容一变文件名就变,适合长缓存;index.html这种入口文件,缓存时间要短,否则更新后用户还是看到老版本:
location /assets/ { expires 30d; add_header Cache-Control "public, immutable"; } location / { expires -1; add_header Cache-Control "no-cache"; }5.2 反向代理与HTTPS:静态站点也能挂接口
静态站点的局限是它只能返回文件,但现实中经常遇到的前后端分离架构是:前端静态页面由Nginx服务,接口请求代理到后端服务。这个场景用Nginx的反向代理能力可以无缝衔接:
location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }这段配置会把所有/api/开头的请求转发到本机8080端口的后端服务。在做前端部署时,浏览器可以直接请求同一个域名下的/api路径,不存在跨域问题,非常干净。
HTTPS这块也不用跳过去。即使现在只是静态站点,我也建议把HTTPS一口气配上。用Let‘s Encrypt的证书是最省事的路径,但如果没有现成域名或不想依赖外部机构,可以先生成自签名证书先顶上。Nginx里生成自签名证书并启用443监听,我个人的做法是分两步:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /etc/nginx/ssl/nginx-selfsigned.key \ -out /etc/nginx/ssl/nginx-selfsigned.crt \ -subj "/CN=your-domain.com"然后在配置里加:
server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt; ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key; }这组配置网上有很多现成模板,核心是证书路径别写错,另外记得443端口同样需要在安全组和防火墙里放行。
5.3 日志切割与意外排错
access.log增长速度快得惊人,尤其是站点一旦有爬虫或异常流量,几天时间就能生成几个GB的日志。日志切割的常规做法是使用logrotate,系统自带的logrotate每天定时处理。Nginx的日志切割原理很简单:先把旧日志改名,再执行nginx -s reopen让Nginx重新打开新的日志文件句柄。apt安装时Nginx已经配套了logrotate配置,通常不需要额外操作。
需要提的是,排错时要善用error.log。我在前面几节反复提到“看日志”,但很多人可能没意识到error.log是Nginx排错的第一手信息源。遇到页面异常,直接:
tail -f /var/log/nginx/error.log它能告诉你的信息比配置文件本身还多,比如文件权限问题、路径不存在的报错、上游连接被拒,error.log里都会有具体的行号和原因。
5.4 离线环境安装Nginx的补充方案
有些生产环境的服务器不联网,或者像银河麒麟这类国产系统,无法直接访问官方源,这时候离线安装就派得上用场。思路是在一台联网的同架构机器上把依赖包和安装包下载下来,拷过去再本地安装。
如果用apt,可以在联网机器上执行:
apt download nginx nginx-common nginx-core libnginx-mod-http-geoip然后把下好的deb包拷到离线机器上,执行dpkg -i *.deb安装。如果是编译安装,则需要把源码包和pcre、zlib、openssl的源码包一并拷过去,在离线机器上解压、configure、make。整个过程不复杂,但对版本依赖关系要仔细,容易漏依赖,建议先在一台联网的干净的机器上把编译依赖列表理清楚。
还有一种方式是把整个nginx的deb安装包连同依赖单独打包成离线仓库(apt-offline工具能做),适合大批量、多台机器重复部署的场景,第一次准备麻烦点,后续复制就能用。
最后再分享一点个人的实战习惯
说实话,Nginx配置静态HTML这件事本身不难,难的是把它放到真实环境里遇到各种边界情况时不慌。我自己经历了几次线上事故之后,摸索出了一套固定的操作习惯,这里做个简单分享。
每次改完配置文件,我一定执行nginx -t再做reload,这两步中间隔了一秒都行,但绝不能省。上线新站点前,我会先在本地用curl -I验证响应头,确认状态码是200再拿域名去访问。我习惯把每个站点的配置文件都放到sites-available,然后通过软链接到sites-enabled,这样即使哪天哪个站点出问题,只要把软链接一删,站点就立即下线,而配置文件还在仓库里,随时能恢复,这个习惯救过我很多次。
还有一个细节,所有静态站点目录的权限我统一用755/644,所有者设置为root也没关系,关键是让Nginx的worker用户有读权限。这个方案在多个线上环境都稳定运行了很久,值得你直接拿来用。
最后再多说一句,如果是给前端项目做部署,构建产物目录里的文件、尤其是带hash的资源文件名,建议保留默认构建配置,不要手动改名。Nginx的缓存策略和文件名是配套设计的,乱改文件名很容易踩到缓存不更新的坑,到时候排查起来会非常头疼。