ARTICLE DETAIL

资讯详情

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

静态网页与H5部署到云服务器:Nginx配置、缓存与排错实战

静态网页与H5部署到云服务器:Nginx配置、缓存与排错实战 把一个纯 HTML 或 H5 的静态网页部署到云服务器上听起来像是要碰一堆命令行和配置文件的活儿但真正上手之后你会发现核心动作其实就三个把文件放到服务器上、装一个能对外提供 HTTP 服务的软件、告诉这个软件去哪里找文件。剩下的都是围绕这三步做的加固和优化。我第一次做这件事的时候在一个 403 权限问题上耗了整整一个下午后来才明白不是配置写错了而是文件属主不对。这篇内容就是把这些年踩过的坑、算过的账、验证过的配置整理出来让刚接触静态网页部署的人能少走弯路也让已经会上传文件但没搞懂原理的人把整条链路串起来。适合读这篇的人大致分三类做前端写完页面不知道怎么上线的、做运营或市场手头有个 H5 活动页需要挂到公网访问的、以及想给自己做个个人主页或者作品集但不想依赖第三方平台的。不管你用的是原生 HTML 加 CSS 加 JavaScript还是用构建工具打包出来的产物只要最终产物是一堆静态文件这套流程都能直接用。1. 部署前先想明白你要放上去的到底是什么1.1 静态网页和动态网站的根本区别静态网页的本质是服务器只负责把文件原样吐给浏览器不参与任何计算。浏览器请求index.html服务器就去磁盘上把index.html读出来加上几个响应头发回去。请求style.css就返回style.css。整个过程没有任何数据库查询没有服务端脚本执行也没有会话状态。这一点决定了部署方式的天差地别。动态网站比如用 Java、PHP、Python 写后端的那种部署时你得考虑运行环境、依赖包、数据库连接、进程守护、内存占用。静态网页完全不需要这些服务器承担的只是文件分发这一个角色。这也是为什么一个配置得当的 Nginx 单机扛住几千并发访问静态文件毫无压力因为它做的只是内存和磁盘之间的搬运。理解这一点的实际价值在于你会知道自己不需要买多高的配置。很多人一上来就担心我这网站能不能扛住访问量然后去买 8 核 16G 的机器实际上一个日均几千访问量的静态站1 核 1G 的入门配置都跑得绰绰有余。真正会卡住你的从来不是 CPU而是带宽这个后面会细算。另外还有一层含义静态站的安全性压力小得多。没有后端代码就没有注入漏洞没有数据库就没有拖库风险。你需要注意的只剩下服务器本身的访问控制、软件版本更新以及别把不该公开的文件比如.git目录、备份文件、源码映射文件一起传上去。1.2 H5 页面在部署上和普通 HTML 有哪些不一样H5 这个词现在被用得很泛严格来说它指的是用 HTML5 相关技术栈做的移动端网页。在部署这件事上它和普通 HTML 页面的差异主要体现在三个地方。第一是视口适配。H5 页面通常是为手机屏幕设计的必须在head里声明 viewportmeta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno如果漏了这行页面在手机上会以桌面宽度渲染然后整体缩小字小到看不清。这不是部署环节的问题但它经常在上线后才被发现因为本地用浏览器开发者工具模拟手机和真机表现有时不完全一致。第二是资源体积敏感。H5 页面常常是活动页、落地页用户在移动网络下打开首屏加载时间直接影响跳出率。所以我通常会把首屏用到的图片压到 200KB 以内CSS 和 JS 做压缩合并非首屏资源做懒加载。部署时如果配了 gzip 压缩和服务端缓存能再省一截。第三是内嵌场景。很多 H5 是嵌在 App 的 WebView 里打开的。这种情况下要注意两件事一是页面里不要用X-Frame-Options: DENY这类会阻止被 iframe 引用的响应头除非你明确知道自己在做什么二是缓存策略要更谨慎App 内嵌页面的缓存清理比浏览器麻烦得多用户经常遇到我明明更新了用户看到的还是旧版的问题。我的做法是给 HTML 文件设置短缓存或不缓存给带哈希指纹的 JS、CSS 设长缓存这样更新时只要 HTML 变了用户自然就拿到新资源了。1.3 上服务器之前本地必须自查的四件事在动手买服务器之前先花十分钟把本地的东西理清楚能省掉后面大量的返工。第一确认入口文件名叫什么。Nginx 默认找index.html如果你的入口叫main.html或者放在子目录里那配置里就得显式指定。这个问题造成的现象是访问域名出现目录列表或者 403很多人第一反应是权限问题其实是没找到入口文件。第二检查所有资源引用路径是相对路径还是绝对路径。本地开发时可能页面放在D:/project/index.html用绝对路径/css/style.css引用浏览器会从盘符根目录开始找本地用文件协议打开时居然也能跑因为浏览器做了兼容处理但传到服务器上放到子目录后就全挂了。我一般要求所有内部资源引用都写成相对路径或者用构建工具统一处理成以/开头的站点根路径。第三把不需要上传的东西排出去。源码目录、.git文件夹、node_modules、.DS_Store、编辑器配置、备份的压缩包这些都不该出现在服务器上。用构建工具的话一般会有个dist或build目录只传这个目录里的内容就对了。第四本地用 HTTP 服务跑一遍。别直接双击 HTML 文件用file://协议打开就以为没问题那样很多行为是不准的。用python3 -m http.server 8000或者npx serve在本地起一个真实的 HTTP 服务测一遍确认所有链接、图片、接口请求都正常再往服务器上放。2. 云服务器怎么选参数、价格与坑2.1 云服务器那几个参数分别管什么打开任何一家云服务商的购买页你会看到一堆参数。对静态站部署来说真正需要认真看的其实只有四个。CPU 和内存决定的是并发处理能力和同时能维持的连接数。Nginx 处理静态文件的资源消耗极低一个请求大概占用几十 KB 内存。1 核 1G 的配置跑一个小型静态站完全够用2 核 4G 基本属于富余。反过来说如果你买的机器大部分时间 CPU 使用率都在 5% 以下那就是买大了。带宽是静态站最关键的参数也是最容易买错的地方。它决定的是单位时间内能往客户端送出多少数据。这个后面单独算。磁盘方面静态站的体积通常很小一个几 MB 到几百 MB 的站配 20G 系统盘足够了。但要注意有些低价套餐给的是高效云盘IOPS 较低如果你后续要跑数据库或者构建任务可能会感觉慢。地域指的是服务器机房所在位置。物理距离直接影响网络延迟。如果你的访问者主要在国内就选国内地域如果面向海外用户就选离用户近的节点。这一点没有太多技巧纯粹是物理规律。至于 32 核 128G 这种配置里的128G指的是运行内存单位是 GB。这个量级是给跑数据库、大数据、AI 推理这类负载用的跟静态网页部署完全不在一个量级上。看到这种参数不用纠结直接跳过就行。2.2 带宽要算一笔账带宽单位是 Mbps兆比特每秒而我们平时说文件大小用的是 MB兆字节两者差 8 倍。所以 5Mbps 带宽的理论最大下载速度是5 Mbps ÷ 8 0.625 MB/s ≈ 640 KB/s现在假设你的 H5 页面首屏需要加载的资源总共是 1MBHTML 加 CSS 加 JS 加首屏图压缩后这是个比较典型的值那么单个用户完整看到首屏大约需要1024 KB ÷ 640 KB/s ≈ 1.6 秒这是在理想情况下的单用户耗时。如果有 10 个用户同时访问每个人分到的带宽就只有十分之一加载时间变成 16 秒体验直接崩掉。所以选带宽的粗略方法是预估峰值同时在线人数 × 单用户页面体积 ÷ 你期望的加载秒数 需要的带宽以 MB/s 为单位再乘以 8 换算成 Mbps。举个例子一个活动页预计峰值 50 人同时打开页面体积 1MB期望 2 秒内加载完50 × 1MB ÷ 2s 25 MB/s 25 × 8 200 Mbps这个数字远超一台入门服务器的带宽上限这时候正确的做法不是去买 200Mbps 带宽很贵而是把图片压小、把资源上传到对象存储配合 CDN 分发让静态资源不占服务器带宽。带宽是云服务器里单位成本最高的资源之一能靠压缩和分发解决的别靠加钱解决。对个人站、企业官网、内部展示页这类流量平稳的场景3Mbps 到 5Mbps 是常见选择。按小时计费还是包月取决于你的使用周期短期活动选按量或按小时长期稳定运行选包月更划算。2.3 买之前确认的三件事确认公网 IP 是独立的还是共享的。有些超低价套餐给的是共享 IP 或者 NAT 后的 IP这种机器你没法直接用 IP 访问也就没法做最基础的上线验证。买之前看清楚描述。确认系统镜像选什么。对新手我一般推荐 Ubuntu 22.04 LTS 或 24.04 LTS社区资料多遇到问题好搜习惯 CentOS 生态的可以选 Rocky Linux 或 AlmaLinux因为 CentOS 7 已经停止维护了继续用会有安全更新问题。镜像在购买时可以选也可以后面重装但重装会清空数据所以第一次就选对比较省事。确认计费方式和续费价格。新用户首年折扣力度通常很大但续费价格可能翻几倍。买之前看一眼续费价别只看首单价。如果你的项目是临时性的按量计费加用完释放可能更合适。另外提醒一件事如果打算用自定义域名访问注册域名和做解析这一步尽量提前做因为解析生效需要时间有时几分钟有时要等更久别等到服务器都配好了才发现域名还没弄。3. 从零把服务器跑起来3.1 第一次登录服务器要做的几件事买完服务器你会拿到公网 IP、登录用户名通常是 root和密码或者在控制台里配置的密钥对。用 SSH 登录ssh root你的服务器IP第一次登录后有三件事建议立刻做。更新系统软件包。新装的系统镜像里的软件版本可能已经落后了先更新一遍# Ubuntu / Debian apt update apt upgrade -y # Rocky / AlmaLinux dnf update -y创建一个普通用户来日常操作。一直用 root 干活风险高一个手滑的命令可能就把系统搞崩了。建个普通用户需要提权时用 sudoadduser deploy usermod -aG sudo deploy # Ubuntu 系 # usermod -aG wheel deploy # RedHat 系配置密钥登录并关闭密码登录。密码登录会被自动化脚本暴力尝试换成密钥登录能挡掉绝大部分噪音。本地生成密钥对ssh-keygen -t ed25519然后把公钥内容追加到服务器的~/.ssh/authorized_keys里。确认密钥能正常登录之后再去改/etc/ssh/sshd_config把PasswordAuthentication设为no重启 SSH 服务。注意改 SSH 配置时千万别关掉当前这个连接再改。一定要另开一个终端窗口测试新配置能登录成功再关闭旧窗口。很多人改完配置重启服务结果新连接连不上、旧连接也断了只能去控制台走 VNC 救援。3.2 装一个 Nginx把目录规划清楚提供静态文件服务的软件有好几个选择Nginx、Apache、Caddy、Lighttpd。我几乎总是选 Nginx原因是它处理静态文件的性能好、内存占用低、配置语法直观、社区资料多而且几乎所有的部署教程都以它为例出问题了容易找到答案。安装# Ubuntu / Debian apt install nginx -y # Rocky / AlmaLinux dnf install nginx -y systemctl enable nginx systemctl start nginx装完之后在浏览器访问http://你的服务器IP能看到 Nginx 的默认欢迎页说明服务起来了。如果访问不了先检查云服务商控制台里的安全组规则有没有放行 80 端口这是新手最容易忽略的一步。安全组相当于云平台层面的一道防火墙跟服务器系统内部的防火墙是两回事两道都要放行才行。接下来规划网站目录。我习惯用/var/www/站点名这个路径比如/var/www/mysite。这个约定的好处是不同站点分开放互不干扰删除或者迁移的时候直接操作整个目录就行不会牵扯到系统文件。mkdir -p /var/www/mysite3.3 把本地文件送上去的四种方式方式一scp适合偶尔传一次。语法简单直接scp -r ./dist/* root你的服务器IP:/var/www/mysite/-r表示递归传目录./dist/*表示传 dist 目录下的内容。缺点是每次全量传输文件多的时候慢而且不会删除服务器上已经废弃的旧文件。方式二rsync适合反复更新。这是我日常用得最多的方式因为它只传有变化的部分还能同步删除rsync -avz --delete ./dist/ root你的服务器IP:/var/www/mysite/参数含义-a保留权限和时间戳-v输出详细过程-z传输时压缩--delete让服务器端和本地完全一致本地删掉的文件服务器上也删掉。注意源路径./dist/末尾的斜杠不能省带斜杠表示同步这个目录里的内容不带则表示把这个目录本身同步过去结果完全不同。方式三宝塔之类的可视化面板。图形界面鼠标拖拽上传适合完全不想碰命令行的人。代价是面板本身会占用一部分系统资源而且面板自身也是一个需要持续关注更新的组件。我个人在正式环境里更倾向纯命令行但如果是给非技术人员维护的机器面板确实降低了门槛。方式四Git 拉取。如果你用 GitHub、GitLab 或者自建 Git 服务管理代码可以在服务器上装 Git然后git clone到网站目录更新时git pull就行。这种方式的好处是版本可追溯回滚就是切一个 commit。缺点是服务器上会保留.git目录要在 Nginx 配置里禁止访问这个路径否则你的全部源码都可能被人下载走。location ~ /\.git { deny all; }选哪种方式取决于你的更新频率和技术偏好。一次性交付用 scp频繁迭代用 rsync多人协作且需要版本管理用 Git。4. Nginx 配置让页面真正被访问到4.1 最小可用的 server 配置Nginx 的配置分两层主配置文件nginx.conf和站点级别的配置文件。在 Ubuntu 上站点配置一般放在/etc/nginx/sites-available/然后软链到sites-enabled/在 Rocky 或 AlmaLinux 上放在/etc/nginx/conf.d/下面以.conf结尾。新建一个/etc/nginx/conf.d/mysite.confserver { listen 80; server_name 你的域名或服务器IP; root /var/www/mysite; index index.html; location / { try_files $uri $uri/ 404; } }逐行解释listen 80表示监听 80 端口server_name填域名没有域名就填 IProot指定网站文件所在的根目录index指定默认入口文件location /块里的try_files是核心逻辑——先尝试找请求路径对应的文件找不到就尝试作为目录处理再找不到就返回 404。改完配置后不要直接重启先用测试命令检查语法nginx -t输出syntax is ok和test is successful才算通过。然后重载配置systemctl reload nginx用reload而不是restart前者是平滑重载不会中断正在进行的连接。这是个好习惯值得一开始就养成。4.2 缓存、压缩、HTTPS 三件套怎么加最小配置能跑通之后接下来做三件优化成本很低但收益明显。开启 gzip 压缩。文本类资源压缩后通常能小 60% 到 80%传输时间直接降下来。在http块或server块里加gzip on; gzip_min_length 1k; gzip_comp_level 5; gzip_types text/plain text/css application/javascript application/json image/svgxml; gzip_vary on;gzip_comp_level取值范围 1 到 9级别越高压缩率越高但 CPU 消耗越大。5 是个比较均衡的值实测下来压缩率和开销的性价比最好。注意不要对已经是压缩格式的文件jpg、png、woff2、mp4开 gzip压不动还白费 CPU。配置静态资源缓存。思路是让不常变的资源在用户浏览器里存久一点减少重复请求location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot)$ { expires 30d; add_header Cache-Control public, immutable; } location ~* \.html$ { expires -1; add_header Cache-Control no-cache; }HTML 不缓存的原因前面提过它是入口必须保证用户拿到最新的版本。JS 和 CSS 缓存 30 天前提是你的文件名里带了内容哈希比如app.3f8a2b.js否则文件更新了用户还是加载旧的。如果用的是不带哈希的固定文件名把过期时间缩短到 1 天甚至几小时比较稳妥。上 HTTPS。现在浏览器对 HTTP 站点会明确标记不安全而且很多功能摄像头、地理位置、Service Worker只在 HTTPS 下可用。用 Lets Encrypt 的证书是免费且自动化的配合 certbot 工具几条命令就能搞定apt install certbot python3-certbot-nginx -y certbot --nginx -d 你的域名certbot 会自动修改 Nginx 配置、申请证书、设置自动续期。证书有效期 90 天自动续期任务会写在系统的定时任务里不用手动管。前提是你得有个域名并且已经解析到这台服务器上。4.3 单页应用和多页站点的路由差异这两种站点的配置有一个关键差异配错了会导致刷新页面就 404。多页站点每个页面都是一个真实存在的 HTML 文件用前面那个try_files $uri $uri/ 404就够了。访问/about.html就返回这个文件访问不存在的路径就返回 404符合直觉。单页应用React、Vue 打包出来的那种所有路由都由前端 JavaScript 处理就不一样了。用户在首页点进/user/profile这个跳转是前端路由完成的服务器完全没参与。但如果用户在这个页面上按了 F5 刷新浏览器就会真的向服务器请求/user/profile这个路径——而服务器上根本没有这个文件于是 404。解决办法是把所有找不到的路径都回退到入口 HTMLlocation / { try_files $uri $uri/ /index.html; }这样服务器永远返回index.html由前端路由去解析当前路径并渲染对应页面。代价是真正的 404 页面也会变成首页如果你的应用需要区分得在前端路由里做兜底处理。注意如果你的站点是子目录部署比如挂在/app路径下try_files的最后一项要写成/app/index.html并且构建时公共路径也要相应配置否则资源会全部 404。这是子目录部署最常见的坑。5. 上线之后排查与维护5.1 常见问题速查表上线过程中遇到的现象就那么几种我把它整理成一张表遇到问题直接对照着查现象最可能的原因排查动作浏览器一直转圈打不开安全组或系统防火墙没放行 80 端口检查云控制台安全组规则、firewall-cmd --list-ports或ufw status403 Forbidden文件权限不对或目录没有执行权限或缺少入口文件检查文件属主和目录权限确认index.html存在404 Not Found路径写错、文件没传上去、大小写不匹配用ls确认文件真实存在核对路径拼写页面能开但样式全丢资源引用路径错误被当成绝对路径请求打开浏览器控制台看 Network 里失败的请求 URL中文显示成乱码HTML 没声明字符集或服务器没返回正确编码确认meta charsetutf-8Nginx 里加charset utf-8;改了代码刷新没变化浏览器或 CDN 缓存强制刷新检查缓存响应头502 Bad Gateway一般出现在反向代理场景检查后端服务是否存活部分用户能打开部分打不开DNS 解析未完全生效或各地缓存不一致换网络或换设备测试等待解析扩散这张表覆盖了九成以上的场景。我遇到问题时习惯按网络层 → 服务层 → 文件层的顺序排查先确认端口通不通再确认 Nginx 有没有正常响应最后才看文件层面。这个顺序能从粗到细快速缩小范围比一上来就翻配置文件高效得多。5.2 三类隐形杀手权限、大小写、编码权限问题是最磨人的。Nginx 的工作进程通常以www-dataUbuntu或nginxRedHat 系用户身份运行如果你的网站文件属主是 root 且权限设成了 600Nginx 就读不到直接返回 403。正确的做法是把目录属主改成 Nginx 的运行用户权限设为目录 755、文件 644chown -R www-data:www-data /var/www/mysite find /var/www/mysite -type d -exec chmod 755 {} \; find /var/www/mysite -type f -exec chmod 644 {} \;提示千万不要图省事直接chmod -R 777。这相当于把家门钥匙插在门上任何能访问服务器的人都能改写你的网站内容往页面里挂恶意脚本。权限问题要精准解决不要暴力放开。在 Rocky 或 AlmaLinux 上还有一个额外变量SELinux。即使文件权限完全正确SELinux 也可能拒绝 Nginx 读取/var/www之外的文件。判断方法是临时执行setenforce 0关闭 SELinux 再测一次如果正常了说明就是它。正确做法不是永久关闭而是给目录打上正确的上下文标签chcon -R -t httpd_sys_content_t /var/www/mysite大小写问题在本地开发时几乎不会暴露因为 Windows 和 macOS 的文件系统默认不区分大小写Logo.png和logo.png被认为是同一个文件。但 Linux 服务器上这是两个完全不同的文件。文件引用写成Logo.png而实际文件名是logo.png本地一切正常上线后图片全裂。这类问题的排查方法就是打开浏览器开发者工具看 Network 面板里哪些请求是红色的把它请求的路径和服务器上实际的文件名逐字符对比。编码问题主要是中文。两个地方要检查HTML 的head里有没有meta charsetutf-8以及 Nginx 配置里有没有charset utf-8;。另外文件名本身如果含中文在传输过程中也可能因为编码转换出问题。我的建议是文件名一律用英文小写加连字符不用中文、不用空格、不用大写从源头规避这类麻烦。5.3 让更新变得省事脚本与容器手动敲 rsync 命令更新几次之后你一定会想把它变成一条命令。写个简单的脚本放在本地#!/bin/bash set -e SERVERroot你的服务器IP TARGET/var/www/mysite echo 开始构建... npm run build echo 同步文件... rsync -avz --delete ./dist/ $SERVER:$TARGET/ echo 重载 Nginx... ssh $SERVER systemctl reload nginx echo 部署完成set -e的作用是任何一步出错就立刻停止避免构建失败了还把旧文件删了这种灾难。这个脚本可以进一步接到 Git 钩子或者 CI 流程里实现推送代码就自动部署。如果想让环境更规范可以用 Docker 跑 Nginx。好处是配置和版本都写在文件里换台机器或者重建环境时一条命令就能复现不会出现我本地是好的这类扯皮。一个最小配置长这样services: web: image: nginx:alpine ports: - 80:80 - 443:443 volumes: - ./dist:/usr/share/nginx/html:ro - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./certs:/etc/nginx/certs:ro restart: unless-stoppedro表示只读挂载容器里的进程改不了宿主机上的文件安全边界更清楚。restart: unless-stopped保证服务器重启后容器自动拉起来不用手动干预。启动就是docker compose up -d更新就是重新 rsync 文件再docker compose restart web。如果访问量进一步增长下一步通常是给静态站配 CDN把图片、JS、CSS 这类不变的大文件放到对象存储上让 CDN 边缘节点就近分发服务器只负责返回那一个 HTML 入口文件。这么调整之后服务器带宽的压力能降一个数量级原本 5Mbps 撑不住的场景可能 1Mbps 就够了。我在实际维护这类站点时最深的体会是真正花时间的从来不是第一次配置而是后续的每次更新和每次异常。所以我宁愿在第一次部署时多花半小时把权限理清楚、把缓存策略配对、把部署脚本写好也不愿意在后面每隔几天就手动登服务器折腾一遍。
返回列表