Nginx 是个人网站服务器上最常见的搭档:轻量、稳定、配置直观。但很多人的 Nginx 配置都是"抄来能用就行"——能跑,却说不出每一行的意思,遇到问题也不知道从哪查起。这篇文章把 Nginx 配置从头讲一遍:文件结构、server 与 location 的匹配规则、静态站点的常用配置,以及最容易踩的坑。
文中配置以本站(一个部署在 Nginx 上的纯静态站)为例,所有片段都经过实践验证。下面的示例统一使用占位域名与目录——www.your-domain.com 与 /var/www/your-domain——实际操作时替换成你自己的域名和站点目录即可。系统是 Debian/Ubuntu 系的布局,其他发行版路径略有差异,稍加对照即可。
一、Nginx 怎么读配置
Nginx 的配置不是一个大文件,而是分层的。最核心的是主配置 /etc/nginx/nginx.conf,它定义了进程模型(worker_processes)、事件模型(events)以及最外层的 http 块。而每个网站的配置,通常放在 http 块里通过 include 引入:
# /etc/nginx/nginx.conf(节选)
user www-data;
worker_processes auto; # 一般等于 CPU 核心数
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
include /etc/nginx/conf.d/*.conf; # 通用片段
include /etc/nginx/sites-enabled/*; # 各站点配置(Debian 系布局)
}
Debian/Ubuntu 上的约定是:每个站点一个文件放在 /etc/nginx/sites-available/,然后用软链接把它"启用"到 /etc/nginx/sites-enabled/。这样禁用一个站点只需删软链接,原文件还在,方便回滚:
sudo ln -s /etc/nginx/sites-available/your-domain /etc/nginx/sites-enabled/your-domain
sudo nginx -t # 语法检查
sudo systemctl reload nginx # 平滑重载
习惯:改完配置先nginx -t再reload,不要用 restart。reload 只重载配置、不断开现有连接;restart 会中断所有正在进行的请求。
二、最小的 server 块
每个网站的核心是一个 server 块:它声明"我要监听哪个端口、为哪个域名服务、文件在哪里"。一个纯静态站的最小配置长这样:
server {
listen 80;
server_name www.your-domain.com;
root /var/www/your-domain;
index index.html;
error_page 404 /404.html;
location = /404.html { internal; }
}
逐行解释:
listen 80:监听 80 端口(HTTP 默认端口);server_name:这个站点的域名。Nginx 收到请求后,按Host头里的域名挑选对应的 server 块;root:站点根目录。请求/style.css时,Nginx 去/var/www/your-domain/style.css找文件;index:访问目录时默认找的文件,所以访问/会加载index.html;error_page 404 /404.html:发生 404 时交给我们自己的 404 页面;配一个location = /404.html { internal; }可以防止访客直接访问这个内部页面以外的路径。
没有写 server_name 命中逻辑时,Nginx 会把第一个 server 块当作默认站点。因此如果你有多站配置,记得给不想当默认的那个也写上明确的 server_name。
三、server_name 与 location 匹配规则
整个 Nginx 配置里最值得花时间理解的,是 location 的匹配优先级。规则就五条,从高到低:
= 精确匹配:location = /404.html只匹配这一个路径,优先级最高;^~ 前缀匹配:命中后直接使用,不再继续查下面的正则;- 正则匹配(
~区分大小写、~*不区分):按配置文件里的书写顺序,命中即停; - 普通前缀匹配:在所有普通前缀里取最长的那个;
- 如果以上都没命中,就走
location /这个"兜底"。
静态站最常用的一个技巧,是把图片、CSS、JS 这类静态资源单独匹配出来做缓存:
location ~* \.(svg|webp|png|jpg|jpeg|ico)$ {
expires 30d;
add_header Cache-Control "public";
}
location ~* \.(html|css|js|json)$ {
expires 1h;
}
这里用正则把请求按文件类型分流:图片类缓存 30 天,页面类缓存 1 小时——重新发布后刷新浏览器即可看到更新,同时日常浏览又不至于每次全量下载。
四、root、alias 与 try_files
root 和 alias 是新手最容易搞混的一对。记住一句话:root 会拼接 location 的路径,alias 不会。
# root:/img/a.png → 实际找 /var/www/site/img/a.png
location /img/ {
root /var/www/site;
}
# alias:/img/a.png → 实际找 /var/www/static/a.png
location /img/ {
alias /var/www/static/;
}
对纯静态站来说,还有一个实用指令 try_files:按顺序尝试找文件,都找不到就走最后一个兜底。它的经典用途是让"不存在的路径"统一交回首页或 404:
location / {
try_files $uri $uri/ =404;
}
本站这类"只有真文件"的站,用 =404 结尾最合适:文件不存在就直接交给 error_page 处理,不产生多余的转发。
五、开启 gzip 压缩
文本类资源(HTML、CSS、JS、JSON、SVG)压缩后能小 60%–80%,是性价比极高的一行配置。在 http 块(或 server 块)里开启:
gzip on;
gzip_min_length 1k; # 小于 1k 的文件不压,压了反而更大
gzip_comp_level 6; # 1-9,6 是体积与 CPU 的平衡点
gzip_types text/plain text/css application/javascript application/json
image/svg+xml application/xml;
gzip_vary on; # 输出 Vary: Accept-Encoding
注意:图片(jpg/png/webp)本身已是压缩格式,再开 gzip 只会浪费 CPU,所以不在 gzip_types 里列它们。
六、安全响应头:add_header 与它的坑
给响应加安全头,是让网站"看起来专业、用起来安全"的重要一步。本站 HTML 里已经用 meta 写了一份 CSP 作为静态托管的兜底,但服务器响应头优先级更高,正式部署建议在 Nginx 里再配一份:
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Resource-Policy "same-origin" always;
# CSP:值与页面 meta 保持一致即可(按实际引用的 CDN 调整;本站只引用 cdnjs,图片全部为站内资源)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; img-src 'self' data:; font-src 'self' https://cdnjs.cloudflare.com; connect-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self';" always;
这里有一个几乎人人踩过的坑:add_header 的继承规则。server 层(或 http 层)定义的 add_header,只有当某个 location 里没有定义任何 add_header时才会被继承;一旦 location 里自己写了一个 add_header,server 层的就全部失效。所以上面第三节给静态资源加 Cache-Control 的 location,会"丢掉"所有安全头——解决办法是把安全头也写进那些 location,或者把安全头统一放在一个被 include 的片段里。
验证方式很简单,用 curl 看响应头:
curl -I http://www.your-domain.com/
# 预期能看到上面配置的 X-Frame-Options、CSP 等
七、日志:网站的另一双眼睛
Nginx 默认把访问日志写在 /var/log/nginx/access.log,错误日志在 /var/log/nginx/error.log。访问日志默认格式 main 大致是:
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
日常最常用的两个动作:一是看某段时间的访问量,二是排查"为什么页面打不开"。页面 500/502 时第一件事就是看 error.log,而不是瞎猜:
sudo tail -f /var/log/nginx/error.log # 实时看错误
sudo tail -n 100 /var/log/nginx/access.log
sudo grep " 404 " /var/log/nginx/access.log | tail -20 # 谁在访问不存在的页面
结合上一篇文章里的服务器安全实践,定期瞄一眼 404 日志还有个额外好处:很多扫描器就是在不断探测不存在的路径——异常密集的 404 往往意味着有人在对你的服务器做探测。
八、常见坑速查
- 改完没生效:多半忘了 reload;先
nginx -t确认语法,再systemctl reload nginx; - 别的站的配置污染了我的站:确认每个 server 块都写了唯一的
server_name,否则请求会落到"第一个 server"; - 安全头时有时无:八成是 location 里写了 add_header,把上层安全头"顶掉"了(见第六节);
- 404 页面样式全丢:检查 error_page 指向的路径、以及该页面的 CSS 相对路径是否在站点根下;
- 想限制上传/请求体大小:用
client_max_body_size 10m;(纯静态站通常用不到,反向代理时常见)。
结语
到这里,一个静态站点的 Nginx 配置从结构、匹配规则到缓存、压缩、安全头就都齐了。这套配置的完整形态,其实就是本站 README 里那份 Nginx 示例的来历——如果你照着自己配一遍,再看 README,每一行都能对上号。
还没讲的部分是 HTTPS:用 certbot 签免费证书、把 80 端口跳转到 443、给安全头补上 HSTS——那是让地址栏亮起小锁的最后一步,留到下一篇。在那之前,如果你还没做过服务器侧的入口防护,建议先读这篇《服务器 SSH 加固实践》,把门锁好再装修。