静态站的服务器配置,大部分人抄来的模板里塞了一堆用不上的东西:反向代理、负载均衡、PHP 转发…… 我这个站连后端都没有,那些全是噪音。下面这份是我实际在跑的配置,删到剩骨头, 但该有的安全项都在。
先说前提
假设域名已经解析到服务器 IP,证书已经签好,站点文件放在 /var/www/mysite。
证书我用的是 acme.sh 签的免费证书,存放在 /etc/nginx/ssl/ 下。
如果是第一次装 Nginx,在 Ubuntu/Debian 上是:
sudo apt update
sudo apt install -y nginx
sudo systemctl enable --now nginx
CentOS / 阿里云 Linux 换一下包管理器就行,注意 CentOS 8 之后默认源里的 Nginx 版本偏老, 建议加官方源。
完整配置
新建 /etc/nginx/conf.d/mysite.conf(不要动 nginx.conf 主文件),
内容如下:
# ---------- HTTP:全部跳转到 HTTPS ----------
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# 证书续期的验证目录要放行,否则续签会失败
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# ---------- HTTPS:静态站主体 ----------
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
root /var/www/mysite;
index index.html;
# 证书
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 基础安全头
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header Referrer-Policy strict-origin-when-cross-origin;
# gzip:文本类才需要压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/javascript
application/json image/svg+xml;
# HTML 不缓存,保证改完立刻生效
location ~* \.html$ {
add_header Cache-Control "no-cache, must-revalidate";
}
# 静态资源长缓存(文件名不变就不该变)
location ~* \.(css|js|svg|png|jpg|jpeg|webp|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# 禁止访问隐藏文件(.git / .env 之类)
location ~ /\. {
deny all;
return 404;
}
# 404 兜底
error_page 404 /404.html;
location = /404.html {
internal;
}
}
存盘后先测语法,通过了再重载。这一步千万别省——配置写错直接 reload,站会当场挂掉。
sudo nginx -t
sudo systemctl reload nginx
几个容易踩的点
1. 证书续期的验证目录必须放行
我在 HTTP 那个 server 块里单独放行了 /.well-known/acme-challenge/。
如果整站 301 到 HTTPS 而没有这个例外,acme.sh 的续签会一直失败,
而失败通常不会立刻发现——等到三个月后证书过期,浏览器一片红,才知道出事。
续期本身交给定时任务,acme.sh 装完会自动加一条 cron,可以用
crontab -l 确认。手动测试续期:
acme.sh --renew -d example.com --force
2. HTML 一定不要设长缓存
静态站最烦人的体验是:内容改了,用户看到的还是旧的。
原因是浏览器把 HTML 缓存住了。我的做法是 HTML 用 no-cache,
CSS 和图片用 30 天。这样内容立刻生效,样式资源又不浪费带宽。
代价是每次改样式,用户可能还在用缓存里的旧 CSS。解决办法是给文件名加版本号,
比如 style.css?v=2——注意这时候 immutable 就要去掉,否则会冲突。
简单点就直接改文件名,style-2.css。
3. http2 的写法变了
Nginx 1.25.1 之后,listen 443 ssl http2; 这种写法会报 warning,
官方推荐改成独立的 http2 on;。老版本不认这个指令,会报 unknown directive。
先看一眼版本:
nginx -v
低于 1.25.1 就把 http2 on; 删掉,改回 listen 443 ssl http2;。
4. 根目录权限
Nginx 的 worker 进程通常以 www-data(或 nginx)身份跑。
如果站点目录属主是 root 且权限是 700,会直接返回 403,日志里只写 Permission denied,
很容易误以为是配置问题。修一下:
sudo chown -R www-data:www-data /var/www/mysite
sudo find /var/www/mysite -type d -exec chmod 755 {} \;
sudo find /var/www/mysite -type f -exec chmod 644 {} \;
要不要上 CDN
我的判断是不需要。这个站的页面加起来不到 100KB,服务器在国内, 访问速度本来就够。上了 CDN 反而多一层要维护的东西——缓存刷新、回源配置、HTTPS 证书同步, 出问题时排查链路还变长了。
如果哪天访问量真的大起来,或者有大量图片,再考虑。现在没必要。