ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

静态网页部署:Nginx配置、HTTPS接入与自动化上线

静态网页部署:Nginx配置、HTTPS接入与自动化上线 现在很多做前端的朋友、做外包的、还有用 Hexo 之类工具写博客的人都会遇到同一个坎本地双击index.html打开一切正常等到要把 html/h5 静态网页部署到服务器上就突然不知道该从哪儿下手。这篇文章就是把这套流程从头到尾拆开讲一遍包括静态网页的本质、云服务器的选型账、Nginx 配置怎么写、H5 响应式页面部署时容易踩的坑、上线之后 403/404/白屏怎么排查以及后面怎么用脚本和流水线把手工拖文件这件事彻底干掉。不管你是刚学完 HTMLCSSJS 想给作品找个落脚点的新手还是手上有几个企业静态站点要维护的从业者都能照着本文的步骤直接抄。我这些年经手过的静态站点不算少从最早用 FTP 工具一拖半小时到后来写脚本、走流水线最后一条命令上线中间踩的坑基本都踩过一遍。所以下面的内容不会是教科书式的第一步第二步第三步而是把每一步背后的原因讲清楚——为什么要这么做、不做会出什么问题、有没有更省事的替代方案。1. 静态网页部署到底在部署什么1.1 静态站点与动态站点的分界线在哪先把概念理清楚不然后面选方案会一直犹豫。所谓静态网页指的是服务端不做任何逻辑计算用户请求什么文件服务器就把这个文件原封不动地吐回去。你的 HTML 文件、CSS、JS、图片、字体、视频本质上都是一堆躺在磁盘上的普通文件服务器扮演的角色就是个文件分发员。而动态站点不一样用户请求user?id3后端要先连数据库、查数据、拼模板、渲染最后才吐出一段 HTML。这个过程需要运行环境、需要进程常驻、需要处理并发和内存。区分清楚这一点直接决定了后面要不要装数据库、要不要配反向代理、要不要考虑进程守护。纯静态站点你连 Node、Java、PHP 都不用装一个 Nginx 就够了Nginx 处理静态文件的性能极强一台 1 核 1G 的机器扛住日均几万 PV 的纯静态访问完全没压力。有一种情况要特别说明现在很多项目用 Vue、React 打包出来的产物本质也是静态文件但它是单页应用路由靠前端 JS 控制。它一样属于静态网页的范畴部署方式也是一样的区别只在于 Nginx 需要多加一行配置来处理路由回退这个后面第 4 章会细讲。1.2 三条主流路线的取舍托管平台、对象存储、自建服务器静态站点的落地方式业内常见的就是三条路线各有各的适用场景我把它整理成一张对照表你按自己的实际情况对号入座路线典型形态优点局限适合谁代码托管平台的 Pages 服务推代码自动构建发布零成本、自带 HTTPS、自带 CDN无法自定义后端、部分平台访问速度一般个人博客、开源项目演示对象存储 CDN上传文件到存储桶绑定域名便宜、抗压能力强、扩容无感配置项多、缓存刷新有延迟中小型静态站点、活动页自建云服务器 Nginx自己买机器自己配完全可控、能顺手跑别的服务需要自己运维、要处理安全更新想学运维、要长期自用、需要同机跑其他服务我个人的建议是这样如果你只是想给简历加一个作品链接走第一条路线最省事如果是公司活动页这种临时性强、流量可能瞬间爆发的走对象存储加 CDN 更稳如果你想真正搞明白网页是怎么跑到公网上去的或者服务器上还要顺便跑点别的东西那就老老实实买一台云服务器自己配一遍。这篇文章的重点放在第三条路线上因为前两条基本是点点鼠标过程没什么可讲的。1.3 新手最容易卡住的三个点我带过不少人入门发现卡住的地方高度集中就三个第一是搞不清文件放哪。很多人登录服务器之后完全不知道网页文件应该扔进哪个目录随便找了个/root就丢进去结果访问 403。这里要记住一个原则Web 根目录应该是一个独立的、权限明确的目录常见约定是/var/www/下面按站点分文件夹。第二是分不清服务器上的 Nginx 和本地的区别。本地你是直接双击文件浏览器读的是本地磁盘服务器上是 Nginx 读到请求之后去配置里写的root目录找对应文件。中间多了一层映射关系配置错了就是 404。第三是改完文件不生效就开始怀疑人生。绝大多数情况是缓存问题要么是浏览器缓存要么是 CDN 缓存要么是 Nginx 压根没重载配置。这个排查思路后面单独开一节讲。2. 上线前的准备工作2.1 云服务器怎么选配置、带宽与计费的账先说配置。静态站点对 CPU 和内存的需求极低因为 Nginx 处理一个静态文件请求基本就是读磁盘 发网络包内存占用几乎可以忽略。所以 1 核 1G 或者 2 核 2G 的入门机型对于个人站点、企业官网这类场景完全够用。别一上来就买 8 核 16G那是给数据库和计算密集型服务准备的。真正需要认真算的是带宽。这里给你一个可以直接套用的估算方法云服务器的带宽单位是 Mbps1 Mbps 的理论下载速度是 128 KB/s。假设你的首页连同 CSS、JS、图片总共 300 KB这已经算偏大的了优化好的移动端首页通常控制在 200 KB 以内那么1 Mbps 带宽128 KB/s ÷ 300 KB ≈ 每秒 0.43 个完整页面3 Mbps 带宽384 KB/s ÷ 300 KB ≈ 每秒 1.28 个完整页面5 Mbps 带宽640 KB/s ÷ 300 KB ≈ 每秒 2.13 个完整页面注意这是每个用户完整加载一次的粗略换算。实际访问中浏览器会复用连接、图片会懒加载、静态资源会被浏览器缓存所以真实能支撑的日访问量要比这个数字高不少。按 3 Mbps 估算日均几千 PV 是没问题的。如果你的站点要面向较大流量或者首页资源特别重那就别硬扛带宽直接上 CDN把静态资源分发出去源站带宽压力立刻就下来了。至于计费方式包年包月适合长期稳定的站点按量付费适合临时测试。这里有个小经验新用户的首年优惠力度通常很大但续费价格会回到原价。所以我一般建议先按月或者按季度买用一段时间确认稳定了再考虑长周期。另外系统盘 40G 起步完全够用纯静态站点占用的空间可能连 100M 都不到。2.2 本地文件整理与路径规范上传之前先在本地把文件结构理清楚这一步偷懒后面在服务器上就得加倍还回来。一个规范的静态站点目录大概长这样my-site/ ├── index.html ├── about.html ├── css/ │ └── main.css ├── js/ │ └── app.js ├── images/ │ └── banner.png └── favicon.ico这里有几个必须检查的点都是血泪教训第一文件名大小写。Windows 和 macOS 的文件系统默认不区分大小写但 Linux 服务器是严格区分的。你在本地写img srcImages/Banner.PNG本地测试一点问题没有传到 Linux 上就是一张裂图。上传前用编辑器全局搜一遍把引用路径和实际文件名的大小写对齐。第二路径用相对路径别用绝对路径。link href/css/main.css这种以斜杠开头的写法意味着从域名根目录开始找。如果你的站点是部署在子目录比如example.com/demo/下这个斜杠就会直接把你带到 404。稳妥的写法是href./css/main.css或者干脆hrefcss/main.css。第三中文文件名和空格。Linux 上处理中文文件名会有编码问题空格在 URL 里要转义成%20很容易出岔子。养成习惯所有资源文件一律用小写英文加连字符命名比如product-list-banner.png。第四清理开发残留。本地项目里的.git目录、node_modules、psd源文件、.DS_Store这类东西一个都别往服务器传既浪费带宽又占空间.git目录暴露在公网上还有源码泄露的风险。2.3 服务器登录方式与必要的信息核验购买完成后服务商控制台会给你公网 IP、初始账号Linux 通常是root和密码或者让你下载一对密钥文件。第一次登录用 SSH 工具Windows 上可以用 PowerShell 自带的 ssh 命令macOS 和 Linux 直接开终端ssh root你的服务器公网IP如果是密钥登录把密钥文件权限设置好再连chmod 600 ~/Downloads/my-key.pem ssh -i ~/Downloads/my-key.pem root你的服务器公网IP首次连接会提示是否信任该主机指纹输入yes回车就行。这一步的原理是 SSH 会校验服务器指纹防止中间人攻击第一次连接因为没有记录所以需要你手动确认一次之后就记住了。这里顺带提一句如果你的站点要用域名访问服务商控制台里会有一系列信息核验流程要走完按着控制台的引导一步步操作即可这里不展开。测试阶段完全可以先用 IP 访问等页面跑通了再处理域名的事不要一上来就把两件事搅在一起出问题了都不知道是哪一环的锅。3. 环境搭建实操让 Nginx 跑起来3.1 首次登录后的基础加固新机器到手别急着装软件先把几个基础安全项处理掉。这不是杞人忧天公网 IP 上暴露 22 端口的机器每天被扫描器撞密码的次数可能上百次。第一件事创建普通用户并禁用 root 直接登录。日常操作没必要用最高权限一个手滑rm -rf就是灾难adduser deploy usermod -aG sudo deploy # Debian/Ubuntu 系 # CentOS/Rocky 系用usermod -aG wheel deploy第二件事配置密钥登录。在本地生成密钥对把公钥传到服务器ssh-keygen -t ed25519 -C deploymyserver ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy你的服务器IP验证能用密钥登录之后再去编辑/etc/ssh/sshd_config把PasswordAuthentication改成no重启 SSH 服务。注意顺序一定要先验证密钥能登录成功再关密码登录顺序反了就是把自己锁在门外。第三件事防火墙只放行必要端口。Web 服务要用的 80 和 443加上你自己的 SSH 端口其他一律关掉sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable如果用的是 CentOS 系对应的命令是firewall-cmd --permanent --add-servicehttp那一套。另外别忘了服务商控制台里还有一层安全组那是云平台自己的防火墙两层都要放行才生效。我见过有人服务器内部防火墙关了但安全组的 80 端口没开折腾一下午以为是 Nginx 坏了。注意修改 SSH 端口能减少扫描噪音但一定要先在安全组里放行新端口确认新端口能连上之后再改配置否则同样会把自己关在门外。3.2 安装Nginx并验证服务基础加固做完装 Nginx。Debian/Ubuntu 系sudo apt update sudo apt install -y nginxCentOS/Rocky/AlmaLinux 系sudo dnf install -y nginx sudo systemctl enable --now nginx装完之后验证三件事服务是否在跑、端口是否在听、返回是否正常。systemctl status nginx ss -tlnp | grep :80 curl -I http://localhostsystemctl status显示绿色的active (running)ss能看到 80 端口处于 LISTEN 状态curl -I返回HTTP/1.1 200 OK这三个都过了说明 Nginx 本身没问题。这时候在浏览器里输入服务器的公网 IP应该能看到 Nginx 的默认欢迎页。如果浏览器打不开但curl localhost是通的说明问题出在网络层回去检查安全组和防火墙。如果curl localhost都不通那就是 Nginx 本身没起来去看/var/log/nginx/error.log。关于配置文件的位置不同发行版习惯不一样心里要有数发行版主配置站点配置目录默认站点根目录运行用户Debian/Ubuntu/etc/nginx/nginx.conf/etc/nginx/sites-available//var/www/htmlwww-dataCentOS/Rocky/etc/nginx/nginx.conf/etc/nginx/conf.d//usr/share/nginx/htmlnginx搞混这个表后面写配置的时候会找不到文件、权限也对不上。3.3 上传文件与目录权限处理现在把本地整理好的文件传上去。先在服务器上建目录sudo mkdir -p /var/www/mysite从本地用 rsync 上传比 scp 更好用它支持增量同步第二次上传只传改动的文件rsync -avz --delete ./my-site/ deploy你的服务器IP:/var/www/mysite/参数含义值得说明一下-a是归档模式保留权限和时间戳-v输出详细信息方便你看传了哪些文件-z传输时压缩文本文件能省不少带宽--delete是让目标目录和源目录保持一致源里删掉的文件目标里也删掉避免旧文件残留导致访问到过期内容。这个参数很好用但用的时候路径末尾的斜杠千万别写错./my-site/和./my-site在 rsync 里含义完全不同前者是同步目录内容后者是同步目录本身写错了可能把整个目标目录结构搞乱。接下来是权限这是 403 错误的第一大来源。原则很简单目录给 755文件给 644属主给 Nginx 的运行用户。sudo chown -R www-data:www-data /var/www/mysite # Debian/Ubuntu sudo chown -R nginx:nginx /var/www/mysite # CentOS/Rocky sudo find /var/www/mysite -type d -exec chmod 755 {} \; sudo find /var/www/mysite -type f -exec chmod 644 {} \;为什么是 755 和 644755 表示属主可读写执行、其他用户可读可执行目录需要执行权限才能被进入和遍历644 表示属主可读写、其他用户只读网页文件只需要被读取不需要执行权限。更宽松的 777 看着省事实际上是个隐患任何被上传的脚本文件都会获得执行权限等于给攻击者留门。3.4 站点配置文件怎么写这是整个部署过程的核心。新建一个站点配置文件sudo vim /etc/nginx/conf.d/mysite.conf # CentOS/Rocky sudo vim /etc/nginx/sites-available/mysite # Debian/Ubuntu内容如下server { listen 80; listen [::]:80; server_name _; root /var/www/mysite; index index.html index.htm; access_log /var/log/nginx/mysite.access.log; error_log /var/log/nginx/mysite.error.log; location / { try_files $uri $uri/ 404; } location ~* \.(?:css|js|jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf)$ { expires 30d; add_header Cache-Control public, max-age2592000; access_log off; } location ~ /\. { deny all; } }逐块解释一下为什么这么写。server_name _;表示匹配任意域名测试阶段用 IP 访问也能命中。等你绑定了域名把它改成server_name example.com www.example.com;。root指定网站根目录Nginx 会把请求的 URI 拼接在这个路径后面去找文件。请求/css/main.css实际读的就是/var/www/mysite/css/main.css。这就是为什么你的目录结构必须和 HTML 里的引用路径对得上。index指定默认首页。访问example.com/的时候Nginx 会依次尝试index.html、index.htm找到就返回。try_files $uri $uri/ 404;这行是精髓。它的执行逻辑是先按请求的 URI 找文件$uri找不到就当成目录找$uri/再找不到就返回 404。对于多页站点这样写就够了。单页应用需要改成回退到index.html第 4 章会讲。最后那个location ~ /\.是拒绝访问所有以点开头的文件防止.git、.env这类敏感文件被直接下载。这个块看着不起眼但它是真实存在的常见泄漏点务必加上。Ubuntu/Debian 系改完还需要建软链接启用站点sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite sudo rm -f /etc/nginx/sites-enabled/default改完配置一定要先做语法检查再重载sudo nginx -t sudo systemctl reload nginxnginx -t会告诉你配置有没有语法错误、在哪个文件哪一行。养成习惯先测再载。因为reload是热重载不会中断现有连接但如果配置本身有语法错误reload会失败服务会继续用旧配置跑——这反而是好事但你会以为改了却没生效白白排查半天。所以看到syntax is ok和test is successful两行都出来了再去 reload。到这里就可以用浏览器访问 IP 看效果了。看到自己的页面出来了说明从文件系统到网络层的整条链路已经打通。4. 性能与体验优化4.1 开启gzip压缩与静态资源缓存页面能打开只是及格线真正影响体验的是加载速度。两个最直接的优化项压缩和缓存。gzip 压缩能把文本类资源的体积压掉 60% 到 80%。一个 100 KB 的 JS 文件压缩后可能只有 25 KB对移动端网络来说差别很大。在 server 块里加上gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 5; gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml image/svgxml;几个参数的选择理由gzip_min_length 1024是因为太小的文件压缩后可能反而变大gzip 头本身有开销1 KB 以下的文件不压缩更划算gzip_comp_level 5是速度和压缩率的平衡点级别调到 9 能多压几个百分点但 CPU 消耗会陡增普通站点完全没必要gzip_types里不要包含图片和视频jpg、png、mp4 这些本身就是压缩格式再压一遍几乎没效果纯粹浪费 CPU。顺便说一句如果用了 HTTPS可以考虑开 Brotli 压缩它的压缩率比 gzip 更好但需要额外的模块支持很多发行版的 Nginx 默认没编译进去需要额外装模块或者换用带 Brotli 的构建版本。进阶玩家可以折腾新手先用 gzip 就够。缓存这块上面配置里的expires 30d配合Cache-Control头会告诉浏览器把静态资源在本地存 30 天。用户第二次访问的时候这些文件直接从本地读一个请求都不发出去页面基本是秒开。但缓存有个副作用必须处理如果你的 HTML 里引用的还是main.css这种固定名字改了样式之后用户可能还在用旧缓存。解决办法是给文件名加指纹哈希现代构建工具webpack、Vite都内置了这个功能打包出来会是main.a3f9c2.css内容变了文件名就变了缓存自然失效。如果你的项目是手写的没有构建流程那至少要保证index.html本身不被强缓存把它的缓存时间设短一点location /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; }no-cache不是不缓存而是每次使用前都要跟服务器确认。这样既省了流量又保证内容及时更新。这个概念很多人搞混值得记一下。4.2 H5响应式页面部署时的额外注意点H5 页面和普通 PC 页面在部署层面的差异主要来自移动端环境的各种幺蛾子。viewport 声明必须写而且必须写对。很多新手写的是meta nameviewport contentwidthdevice-width, initial-scale1.0如果要做 H5 响应式布局这就是标配。但如果是那种设计稿宽度固定的 H5 营销页比如 750px 设计稿更常见的做法是用 JS 动态设置font-size然后全程用 rem或者干脆用vw单位。这两种方案的部署层面没有区别只是文件内容不同。苹果设备的 100vh 问题。在 Safari 和很多 App 内嵌 WebView 里100vh会包含地址栏的高度导致页面出现意外滚动。稳妥的写法是用100dvh或者用height: 100%配合html, body { height: 100%; }。这个问题本地测试经常发现不了上线到真机才暴露。图片要控制体积。移动端网络相对有限一张 2MB 的 banner 图能让首屏加载时间翻好几倍。上线前把图片压缩一遍优先用 WebP 格式再加loadinglazy让首屏以下的图片延迟加载。Nginx 层面可以配合上面说的缓存策略长远看收益很大。App 内嵌 WebView 的缓存问题。这是做 H5 的人绕不开的坑。App 里内嵌的 WebView 往往有自己的缓存策略有时候你在服务器上更新了页面用户在 App 里看到的还是老内容。解决思路是给资源加版本参数main.css?v20240601或者用内容哈希文件名然后发版时同步更新入口 HTML。App 端也可以配合清理 WebView 缓存但那是客户端的事服务端能做的主要就是保证入口 HTML 不被强缓存。4.3 单页应用路由模式的部署差异如果你部署的是 Vue、React 打包出来的单页应用Nginx 配置要多加一行这是新手最容易卡住的地方。单页应用一般有两种路由模式Hash 模式和 History 模式。Hash 模式的地址长这样example.com/#/about井号后面是前端自己处理的服务器只认识example.com/所以Hash 模式不需要改 Nginx 配置直接就能用。History 模式的地址长这样example.com/about看着更干净但问题来了用户在这个地址按 F5 刷新浏览器会真的向服务器请求/about这个路径而服务器上根本没有这个文件直接 404。解决办法就是让 Nginx 在找不到文件的时候把请求交给index.html处理由前端路由去解析路径location / { try_files $uri $uri/ /index.html; }和之前多页站点的配置相比只是把404换成了/index.html。这一行改动解决所有刷新 404 的问题。但这行配置有个容易忽略的副作用它会把所有 404 错误都吃掉了。用户访问了一个真正不存在的资源比如/api/xxx本该返回 404 的现在全都返回 200 加一个 HTML 页面。如果你希望 API 路径保持正常返回就得把 API 的 location 块放在前面单独处理location /api/ { proxy_pass http://127.0.0.1:8080; } location / { try_files $uri $uri/ /index.html; }Nginx 的 location 匹配有优先级规则精确匹配最高其次是最长前缀匹配正则匹配~按配置文件中的顺序来。所以 API 这种更具体的前缀会先命中不会被后面的通配规则抢走。理解这个优先级很多配置了但没生效的问题就解释得通了。5. HTTPS与域名接入5.1 域名解析配置IP 访问太丑也不方便分享这时候该上域名了。在域名服务商的控制台里加一条记录记录类型主机记录记录值TTLA你的服务器公网 IP600Awww你的服务器公网 IP600代表主域名本身www代表二级域名。TTL 是缓存时间单位秒600 表示各地 DNS 服务器缓存 10 分钟。刚配置完可能要等一会儿才生效可以用ping example.com或者nslookup example.com看返回的 IP 对不对。解析生效之后把 Nginx 配置里的server_name _;换成你的域名reload 一下然后用域名访问验证。这里有个自检技巧如果 IP 能访问、域名不能那就一定是解析或者server_name的问题如果两个都不行那是服务端的问题。这样一刀切下去排查范围立刻缩小一半。5.2 免费证书申请与自动续期HTTPS 现在几乎是标配浏览器对 http 站点的不安全提示会严重影响观感某些场景下部分能力也会受限。好消息是证书基本不用花钱了用 Lets Encrypt 配合 certbot 工具几条命令搞定。以 Ubuntu 为例sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d example.com -d www.example.comcertbot 会自动读取你的 Nginx 配置、申请证书、修改配置文件加上 443 端口的监听和证书路径、然后自动 reload。整个过程基本是交互式的问你要不要强制跳转 HTTPS选是就行。证书有效期 90 天但 certbot 会自动创建定时任务续期你只需要验证一下这个机制是通的sudo certbot renew --dry-run systemctl list-timers | grep certbot--dry-run是模拟续期不真正申请证书用来验证流程是否通畅。输出里如果出现 Congratulations, all renewals succeeded说明到期前会自动处理好你基本可以不用管它了。如果出于某些原因需要手工配置证书443 的 server 块大概长这样server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; root /var/www/mysite; index index.html; location / { try_files $uri $uri/ 404; } }同时再加一个 80 端口的 server 块把所有 http 请求 301 跳转到 httpsserver { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }配置 HTTPS 之后有个细节要注意页面里引用的资源地址最好用相对路径或者协议无关的写法。如果硬编码成http://example.com/js/app.js在 https 页面里加载 http 资源会被浏览器当作混合内容拦截控制台报一堆警告资源直接加载失败。这是从 http 迁移到 https 时最常见的问题。6. 上线后问题排查实录6.1 状态码速查表部署完之后出问题第一个动作永远是打开浏览器开发者工具的 Network 面板看请求的 HTTP 状态码。下面是静态站点常见的几种以及对应的排查方向状态码常见原因排查动作403 Forbidden文件权限不对、目录没有 index 文件且未开放目录列表、SELinux 限制检查/var/www/mysite的属主和 755/644 权限404 Not Foundroot 路径写错、文件没传上去、文件名大小写不匹配、URI 拼错直接在服务器上ls一下那个路径看文件到底在不在500 Internal Server ErrorNginx 配置语法错误、权限导致读取失败看/var/log/nginx/error.log的具体报错行502 Bad Gateway纯静态站点极少出现若出现一般是配置里误加了 upstream 代理检查配置里有没有多余的proxy_pass301/302URL 重写规则生效中、结尾斜杠自动补全属正常行为看清楚跳转目标对不对304 Not Modified协商缓存命中浏览器直接用本地副本正常行为不是错误403 是新手遇到最多的一个。除了权限还有一个坑是 SELinux。CentOS/Rocky 系默认开启 SELinux它会限制 Nginx 进程读取它认为不该读的目录。表现就是权限明明给了 755还是 403。可以先用sudo setenforce 0临时关掉验证一下是不是这个原因如果是正确做法是给网站目录打上正确的 SELinux 标签sudo semanage fcontext -a -t httpd_sys_content_t /var/www/mysite(/.*)? sudo restorecon -Rv /var/www/mysitesetenforce 0只是用来定位问题的临时手段别当成最终方案长期关着 SELinux 相当于少了一层防护。6.2 白屏与样式丢失页面能返回 200但一片空白这种情况通常不是服务器的问题而是资源加载失败或者 JS 报错。第一步看控制台Console的报错。如果是一片红色的 404说明资源路径不对。最常见的原因是打包配置里的publicPath或者base设置成了/而你的站点实际部署在子目录下导致所有资源请求都跑到了域名根目录。第二步看 Network 面板里哪个资源红了。如果是 CSS 404页面会变成完全没有样式的裸 HTML这种一眼就能认出来。如果是 JS 404通常表现为白屏因为整个应用的挂载逻辑没执行。第三步确认是不是真的白屏。有些白屏其实是页面已经渲染了只是内容在可视区之外或者某个容器的height: 0。用开发者工具选中 body 看看有没有内容结构能快速区分渲染失败和渲染了但看不见。还有一个容易被忽略的原因跨域问题。如果页面里的 JS 去请求了另一个域名的接口控制台会报 CORS 错误请求被拦截后续逻辑全部中断看起来就像白屏。这个锅其实该后端或者接口配置背但排查的时候要能认出来。静态资源本身不涉及跨域只有 XHR/Fetch 请求才会。6.3 更新不生效的缓存坑这个坑我踩过不止一次明明文件上传了服务器上cat出来也是新内容浏览器里刷新还是老的。按照缓存层级从近到远排查浏览器缓存。用无痕窗口打开或者开发者工具里勾上禁用缓存。如果无痕正常、普通模式不正常那就是浏览器缓存正常现象用户第二次访问会自动更新因为入口 HTML 设了 no-cache。CDN 缓存。如果你前面挂了 CDN需要在控制台手动刷新缓存或者等缓存过期。CDN 的缓存刷新一般几十秒到几分钟生效。Nginx 配置未重载。改完配置忘了systemctl reload nginx。用nginx -T可以打印出当前实际生效的完整配置对比一下就知道载了没有。文件没真正同步上去。rsync 的--delete有时候会因为路径写错而同步到别的地方用ls -la加上md5sum对比本地和服务器的文件指纹一比一个准。服务端多台机器负载均衡。如果前面挂了负载均衡只更新了一台机器的文件用户请求打到另一台就还是旧内容。这种情况需要保证所有后端节点都同步。按这个顺序排查基本十几分钟就能定位到具体是哪一层的问题。7. 自动化部署告别手工拖文件7.1 rsync一键同步脚本手工敲命令总有敲错的时候写个脚本封装起来用起来就稳了。在本地建一个deploy.sh#!/usr/bin/env bash set -euo pipefail SERVERdeploy你的服务器IP TARGET/var/www/mysite/ LOCAL_DIR./dist/ echo 开始同步文件... rsync -avz --delete \ --exclude.DS_Store \ --exclude.git/ \ --excludenode_modules/ \ $LOCAL_DIR $SERVER:$TARGET echo 同步完成验证Nginx配置... ssh $SERVER sudo nginx -t sudo systemctl reload nginx echo 部署完毕set -euo pipefail这三件套值得解释一下-e是任何命令返回非零就立即退出避免错误被忽略后继续往下跑-u是使用未定义变量时报错-o pipefail是管道中任一环节失败都算失败。脚本加上这三行稳定性提升明显。写完给执行权限然后./deploy.sh一键搞定。再用 alias 或者 git 的 post-commit 钩子绑定一下就能做到提交即部署的效果。7.2 提交即部署的流水线思路如果代码托管在平台上可以直接用平台的流水线能力做自动化。核心思路是推送到主分支后触发任务任务里做两件事——构建如果是打包项目然后通过 SSH 把产物同步到服务器。用通用的脚本步骤描述就是# 1. 安装依赖并构建 npm ci npm run build # 2. 写入部署私钥 mkdir -p ~/.ssh echo $DEPLOY_KEY ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H $SERVER_HOST ~/.ssh/known_hosts # 3. 同步产物 rsync -avz --delete ./dist/ $SERVER_USER$SERVER_HOST:/var/www/mysite/关键是把私钥和服务器地址放在流水线的加密变量里不要写死在脚本里。ssh-keyscan这一步很多人会漏掉导致首次连接时卡在指纹确认上超时失败。这套流程搭好之后从前端改一个错别字到线上生效大概是两分钟的事效率提升非常明显。而且因为全程没有手工操作也杜绝了改完忘了传传错目录这类低级错误。7.3 容器化部署静态站点如果你希望环境更干净、迁移更方便可以试试用容器跑静态站点。好处是 Nginx 的版本和配置都封装在镜像里换台机器一条命令就能复现。最简版本直接挂载目录docker run -d \ --name mysite \ --restart unless-stopped \ -p 80:80 \ -v /var/www/mysite:/usr/share/nginx/html:ro \ nginx:alpine--restart unless-stopped保证服务器重启后容器自动起来-v后面的:ro表示只读挂载容器里的进程无法修改宿主机文件安全性更好。如果要自定义配置就写个 DockerfileFROM nginx:alpine COPY nginx.conf /etc/nginx/conf.d/default.conf COPY ./dist/ /usr/share/nginx/html/ EXPOSE 80 CMD [nginx, -g, daemon off;]然后docker build -t mysite:latest .构建docker run起来。每次内容更新重新构建镜像就行配合流水线可以做得很优雅。容器的代价是多了一层抽象出问题了要多看一层日志docker logs mysite。对纯静态站点来说容器带来的收益主要是环境一致性和快速迁移如果只是自己维护一台服务器裸装 Nginx 也完全没问题不用为了容器而容器。8. 几个我踩过的坑和长期维护建议最后分享点实际经验这些是文档里不太会写的东西。日志要定期看别等出事。/var/log/nginx/access.log里能看出很多信息有大量请求 404 的爬虫、有扫描特定路径的探测行为、有明显的异常流量峰值。养成每周瞄一眼的习惯能提前发现不少问题。日志文件长期不清理会撑爆磁盘可以配 logrotate一般发行版默认就有确认一下策略是否符合你的预期即可。服务器要定期更新系统补丁。一条sudo apt upgrade或者sudo dnf upgrade就能完成但我建议先在测试环境验证再上生产尤其是内核更新可能要重启。重启前确认业务能容忍短暂的不可用或者做好切换方案。备份比什么都重要。静态站点的备份特别简单本地有一份完整的源文件服务器上那份本质上是从本地同步过去的。所以真正要备份的是服务器上那些本地没有的东西——Nginx 配置文件、SSL 证书、定时任务。把/etc/nginx/目录打包存一份换服务器的时候能省大量时间。关于监控。个人站点没必要上重量级的监控系统但至少要知道站点是不是活着。最土的办法是用一个定时任务每分钟 curl 一次首页状态码不是 200 就给你发条消息。这个脚本二十行就能写完比装一套监控系统省事得多对个人站点来说性价比极高。关于成本。静态站点真的是最省钱的一类应用了。带宽选小一点配合 CDN机器选最低配一年下来成本可能就是一个午饭钱。千万不要被各种高可用方案吓到个人站点的可用性要求没那么高简单直接就是最优解。最后一点也是我觉得最值得说的把过程记下来。我第一次配 Nginx 的时候改一个参数查半小时文档第二次还是得查。后来我把自己每次的操作命令、遇到的问题、解决方式都记在一个 markdown 文件里现在再遇到类似场景直接翻自己的笔记几分钟搞定。这个习惯带来的效率提升比学任何一个新框架都要大。
返回列表