Nginx 静态站点安全加固实战

很多人觉得"静态站没什么好黑的"。实际不然——静态站点被黑最常见的形态是:

  1. 服务器层面失守(SSH 弱口令、旧版本漏洞)后整站被替换或挂黑链;
  2. Web 服务器配置不当:目录列表开着、.git/ 目录裸奔、备份文件可下载;
  3. HTTP 层缺防护:没有 HSTS 被运营商劫持插广告、没有 CSP 被注入脚本。

静态站没有 SQL 注入、没有反序列化,攻击面天然小,但上面这几条一条都不能少。以下是我给自己站(Nginx + 纯静态文件)做的加固清单,可直接抄。

1. 基础收敛

server_tokens off;          # 隐藏 Nginx 版本号,别帮攻击者缩小漏洞范围
autoindex off;              # 关闭目录列表(默认就是 off,显式写上防手贱)

2. 封禁敏感路径

部署时最容易留下的坑:.git/、编辑器备份文件、.env 之类。一刀切拒绝:

# deny dotfiles and backup leftovers
location ~ /\.(?!well-known) {
    deny all;
    return 404;
}
location ~* \.(bak|swp|sql|tar|gz|zip|log|sh)$ {
    deny all;
    return 404;
}

注意 /.well-known/ 要放行,acme.sh 签证书时 HTTP-01 验证要用。

3. 安全响应头

静态站的标准套装:

add_header X-Content-Type-Options    "nosniff" always;
add_header X-Frame-Options           "SAMEORIGIN" always;
add_header Referrer-Policy           "strict-origin-when-cross-origin" always;
add_header Permissions-Policy        "camera=(), microphone=(), geolocation=()" always;

逐条解释:

  • nosniff:禁止浏览器 MIME 嗅探。没有它,一个上传目录里的文本文件可能被当脚本执行;
  • X-Frame-Options:防点击劫持。纯内容站直接 DENY 也行;
  • Referrer-Policy:外站只会拿到你的域名,拿不到完整路径(路径里可能有敏感参数);
  • Permissions-Policy:明确禁用用不上的浏览器能力,纵深防御。

HTTPS 站再加 HSTS,强制浏览器后续只走 HTTPS:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

提醒:HSTS 一旦下发一年内无法撤销,上之前确认全站(含子域名)都能 HTTPS。

CSP 单独写了一篇:内容安全策略(CSP)从原理到落地。

4. 限流

静态站也会被扫描器和高频爬虫骚扰,给个宽松的全局限流即可:

limit_req_zone $binary_remote_addr zone=general:10m rate=20r/s;

server {
    ...
    limit_req zone=general burst=40 nodelay;
}

正常用户不可能 1 秒打 20 个请求,这个阈值只拦机器。

5. TLS 配置(2026 版)

ssl_protocols        TLSv1.2 TLSv1.3;
ssl_ciphers          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache    shared:SSL:10m;
ssl_session_timeout  1d;

TLS 1.0/1.1 已淘汰多年,不用留。证书用 acme.sh 免费续期即可。

6. 缓存与压缩

既是性能优化也是抗 DDoS 手段——能被浏览器缓存住的请求根本不落到后端:

gzip            on;
gzip_types      text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;

location ~* \.(jpg|jpeg|png|gif|svg|woff2)$ {
    expires     30d;
    add_header  Cache-Control "public";
}
location ~* \.(css|js)$ {
    expires     7d;
    add_header  Cache-Control "public";
}

7. 文件与系统层

HTTP 层之外的三条底线:

# 站点根目录属主与 Nginx worker 分离,worker 只读
chown -R deploy:nginx /var/www/blog
chmod -R 750 /var/www/blog

# 上传/写入只给真正需要的目录
find /var/www/blog -type f -exec chmod 640 {} +
  • Nginx worker 以低权限用户跑(默认 nobody/nginx,别手贱用 root);
  • 站点目录对 worker 只读,发布走独立的部署用户 + rsync/git;
  • 系统层:SSH 禁密码登录、fail2ban 兜底——多数"静态站被黑"其实是 SSH 被爆破。

验证

改完用 curl 逐条检查:

curl -sI https://www.example.com/ | grep -iE 'strict|x-content|x-frame|referrer|permissions'
curl -s -o /dev/null -w '%{http_code}\n' https://www.example.com/.git/HEAD
# 期望:404

再跑一轮 securityheaders.com 扫描,A 级才算过关。这份配置的完整版我放在本站仓库的 deploy/nginx-site.conf,和文章保持同步。

← 返回首页 · 历史归档