云服务器 Nginx 静态托管实录
云服务器 Nginx 静态托管实录
从“能打开”到“能放心给人看”
一台刚装好系统的云服务器,装上 Nginx、把 HTML 文件丢进默认目录,浏览器确实能打开页面。但这只是“能打开”:刷新子路由 404、样式文件被当成纯文本下载、改了文件浏览器还显示旧版、日志里全是扫描器的 404——这些问题我都在同一台机器上踩过一遍。下面这套配置就是按踩坑顺序整理出来的,目标不是炫技,而是让静态站长期稳定、改动可预期。
我的环境是一台 Debian 系云服务器,Nginx 用系统包安装,站点根目录放在 /var/www/site,域名解析已经指向服务器 IP。本文不涉及任何付费服务,也不依赖面板。
第一步:先把目录和权限摆对
很多人第一步就卡在 403。Nginx 的 worker 进程默认以 www-data 用户运行,文件属主如果是 root 且权限过紧,或者目录缺少执行权限,都会报 403。我的做法是目录归部署用户所有、组给 www-data,目录 755、文件 644:
sudo mkdir -p /var/www/site
sudo chown -R deploy:www-data /var/www/site
sudo find /var/www/site -type d -exec chmod 755 {} \;
sudo find /var/www/site -type f -exec chmod 644 {} \;
这里有个容易忽略的坑:只改文件权限、不改目录执行权限照样 403,因为 Nginx 需要沿路径逐级进入目录。排查时先看 error.log,它会明确写是权限问题还是文件不存在,不要凭感觉改配置。
第二步:一个干净的 server 块
不要直接改默认站点配置,我习惯单独建一个文件,sites-available 写好后软链到 sites-enabled,改坏了随时摘掉链接回滚:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/site;
index index.html;
access_log /var/log/nginx/site.access.log;
error_log /var/log/nginx/site.error.log;
location / {
try_files $uri $uri/ =404;
}
}
每次改完先跑 nginx -t 再 reload,不要直接 restart:reload 是平滑重载,不断已有连接;restart 会把正在下载的请求直接掐断。nginx -t 报错时千万别强行 reload,旧配置还在跑,先把语法修好。
第三步:单页应用的路由回退
纯静态多页站用上面的 try_files $uri $uri/ =404 就够了。但如果是前端路由的单页应用(React、Vue 打包产物),直接访问 /about 会 404,因为磁盘上根本没有 about 这个文件——路由是前端在浏览器里解析的。这时要把回退改成:
location / {
try_files $uri $uri/ /index.html;
}
注意这条规则只适合单页应用。给多页站也套上它,真实不存在的页面会返回 200 的首页,搜索引擎和调试都会被误导:明明是 404 的链接,看起来却“正常”。所以先分清站点类型,再选回退策略,这是我踩过最隐蔽的一个坑。
第四步:缓存分层,别让“改了没生效”反复出现
静态资源缓存要分两层写。带哈希的文件名(比如 app.a1b2c3.js)内容变了文件名也变,可以放心长缓存;index.html 本身是入口,必须不缓存,否则新版 JS 文件名已经变了,浏览器还拿着旧 HTML 去找旧文件,直接白屏:
location / {
try_files $uri $uri/ =404;
add_header Cache-Control "no-cache";
}
location ~* \.(js|css|png|jpg|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
上线后改了文件还是旧版,先分清是哪一层:按 Ctrl+F5 强制刷新能好,就是浏览器缓存;换台设备或清缓存还不好,再查是不是 CDN 或 Nginx 的 expires 写得太死。不要一上来就把所有缓存全关掉,那等于让每个请求都回源,流量和延迟都会上去。
第五步:压缩与 MIME,别让浏览器猜
文本类资源开 gzip 能省一半以上的流量,Nginx 自带的 gzip 模块就够用,不用额外装东西:
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
另一个常见症状是 CSS 不生效、JS 被当成文本:多半是 MIME 类型不对。先查 mime.types 是否被 include,进而确认服务器返回的 Content-Type。自己写 location 时不要顺手加 default_type text/plain,它会覆盖掉按扩展名判断的逻辑,样式表和脚本会一起遭殃。
第六步:HTTPS 与安全头,放在最后做
HTTP 跑通、缓存行为验证无误之后,再上 HTTPS。证书用免费的自动化签发工具申请,Nginx 里只保留 443 的 server 块,80 全部 301 跳转到 HTTPS,避免两套内容并存:
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
安全头我只加三条最实在的:X-Content-Type-Options: nosniff 防止浏览器猜 MIME、X-Frame-Options: SAMEORIGIN 防页面被嵌进别人的 iframe、Referrer-Policy: no-referrer-when-downgrade。不要一上来就抄一长串 CSP,写错一条自己的脚本先被拦住,排错成本比收益大。
上线检查清单
每次发布我按这个顺序验一遍,十分钟能跑完:nginx -t 通过、首页 200、子路由按预期返回 200 或 404、样式和脚本的 Content-Type 正确、改版后强制刷新能看到新内容、HTTPS 跳转正常、error.log 里没有新增的权限报错。清单跑完再通知别人访问,比“我这边能打开”可靠得多。
静态托管没有多少高深技术,难的是每一步都可验证、可回滚:配置单独成文件、改动先测试再重载、缓存按文件类型分层、问题先看日志再动手。这套流程跑顺之后,加第二个站点就是复制一个 server 块、换个根目录和域名,十分钟的事。
- 点赞
- 收藏
- 关注作者
评论(0)