最近把自己的博客部署到了云服务器上,Nginx 这关躲不过去,折腾了几天总算把常用的东西摸熟了。网上教程大多是复杂的官方文档,许多功能个人建站根本用不上。我把实际用到的核心配置整理成这篇笔记,都是个人建站最需要的那部分。
一、核心骨架:一个最基础的虚拟主机
先搞清楚一个问题:我在浏览器输入 www.example.com,敲下回车,Nginx 到底干了什么?
用大白话拆解一下:
- DNS 把域名解析到服务器的公网 IP,请求打到这台机器的 80 端口;
- Nginx 接到请求,先看请求头里的
Host字段(也就是你输的域名),拿它去和所有 server 块的server_name逐个比对,找到匹配的那个 server 块。一台服务器可以挂好几个网站,靠的就是这个机制,所以叫「虚拟主机」; - 匹配到之后,Nginx 从
root指定的目录里找文件,路径是root + 你访问的路径; - 如果你访问的是
/(根路径),就按index列出的文件名挨个找,找到第一个为止。
下面是一个能跑起来的最简配置:
server { # 监听 80 端口(HTTP) # server_name 写你的域名,Nginx 靠它区分同一台机器上的不同网站 listen 80; server_name www.example.com;
# 网站文件存放的目录,也就是博客打包后的输出目录 root /var/www/blog;
# 访问根路径时默认找哪个文件,按顺序找,找到第一个就返回 index index.html index.htm;}假设访问 www.example.com/,Nginx 就会去找 /var/www/blog/index.html,找到就返回给浏览器,找不到就是 404。就这么简单,一个静态博客站的最小配置完成了。
顺带一提,配置文件放哪:一般是 /etc/nginx/conf.d/你的站.conf 或者 /etc/nginx/sites-available/ 里建一个再软链到 sites-enabled/,看发行版本,效果一样。
二、反向代理:把本地服务代理出来
静态站直接扔目录里就行,但很多博客程序是动态的。比如我跑了个 Node 服务或者 Docker 容器,它监听在 127.0.0.1:3000,这个端口外网访问不到。怎么让用户通过 80 端口的域名访问到它?
答案就是反向代理:Nginx 在前面收请求,转手塞给本地 3000 端口的服务,再把服务的响应转回给用户。用户全程只跟 Nginx 打交道,完全不知道背后还有个 3000 端口。
标准写法:
server { listen 80; server_name www.example.com;
# 所有请求都代理到本地的 3000 端口 location / { proxy_pass http://127.0.0.1:3000;
# 下面三个请求头非常重要,往下看解释 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}重点说说那三个请求头。加了 proxy_set_header 之后,Nginx 会把这几个字段改写后再转发给后端。
Host:后端程序经常要靠它判断用户访问的是哪个域名(比如生成绝对链接、路由分发)。不传的话,后端看到的 Host 是 127.0.0.1:3000,一些框架会直接懵掉。
X-Real-IP 和 X-Forwarded-For:这是新手最容易忽略的坑。TCP 连接是 Nginx 跟后端建立的,所以后端看到的「客户端 IP」永远是 127.0.0.1。日志里全是 127.0.0.1,想做 IP 封禁、统计地域分布,全都无从下手。而访客的真实 IP,Nginx 是知道的(它直接跟访客通信),这两个头就是把真实 IP 转交给后端的「快递单」:
X-Real-IP:直白,就一个值,当前这次请求的访客真实 IP;X-Forwarded-For:会累积,格式是访客IP, 第一层代理IP, ...,多层代理时能看出完整链路,后端日志程序一般认这个。
不传这两个头,你的后端日志里就只剩一片 127.0.0.1。
三、强制 HTTPS:SSL 证书配置
个人站 HTTPS 已经是主流了,没有的话浏览器还会给标个「不安全」。证书用 Let’s Encrypt 免费申请就行(certbot 一条命令的事),拿到证书文件后配 443:
# 443 端口:真正的 HTTPS 服务server { listen 443 ssl; server_name www.example.com;
# 证书文件路径(certbot 申请的证书就在这类位置) ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 网站内容,和 80 那份一样 root /var/www/blog; index index.html;}
# 80 端口:只干一件事,把所有 HTTP 请求踢到 HTTPSserver { listen 80; server_name www.example.com;
# $host 是用户访问的域名,$request_uri 是路径加查询参数 # 比如 http://example.com/post/1?a=2 会跳到 https://example.com/post/1?a=2 return 301 https://$host$request_uri;}这里说一下为什么用 return 301 而不是 rewrite ^(.*)$ https://... permanent:
return是 Nginx 官方推荐的做法,收到请求直接返回 301,简单直接;rewrite要先做正则匹配和捕获,纯属于多绕一圈,正则写错还会踩坑;- 301 是永久重定向,浏览器和搜索引擎会记住这个跳转,之后直接访问 HTTPS,省一次往返。
一句话总结:能用 return 就别用 rewrite。
四、图片防盗链:保护服务器的流量
防盗链的原理一句话:HTTP 请求头里有个 Referer 字段,记录了「这个请求是从哪个网页发出来的」。正常读者在我的网页上看到图片,Referer 就是我的域名;被别人盗链时,Referer 是对方的域名。所以只要检查 Referer,不是自己家的就拒绝。
server { listen 80; server_name www.example.com; root /var/www/blog;
# 只对图片类静态资源做防盗链 location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
# valid_referers 定义白名单:以下来源算合法 # none:浏览器直接输入图片地址打开(没有 Referer,比如地址栏直接访问、收藏夹) # blocked:Referer 被防火墙之类的代理抹掉了 # server_names:所有 server_name 里写过的域名(含子域名) valid_referers none blocked server_names;
# 如果 Referer 不在白名单里(invalid_referer 变量为真) if ($invalid_referer) { # 直接返回 403,拒绝访问,盗链网站上的图片就是裂图 return 403; } }}想更严的话,把 none 去掉,强制所有请求必须带合法 Referer。但副作用是别人把图片地址复制到浏览器地址栏直接打开也会被拦,一般还是留着 none 比较友好。
五、重定向
常用重定向:裸域名跳 www
很多人两个域名都解析了(example.com 和 www.example.com),但希望统一用一个,对 SEO 也好:
server { listen 443 ssl; server_name example.com; # 只匹配裸域名
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 统一跳到带 www 的域名,保留路径和参数 return 301 https://www.example.com$request_uri;}六、三个我遇到过的坑
坑一:改完配置直接 reload
Nginx 的配置文件是纯文本,任何语法错误都起不来。如果改完直接 nginx -s reload,有错误会导致 reload 失败,运气差碰会直接挂掉。养成肌肉记忆:
# 第一步永远是测试语法,会精确告诉你哪一行错了nginx -t
# 看到 syntax is ok 和 test is successful 才执行 reloadnginx -s reload坑二:proxy_pass 末尾那个斜杠
# 情况 A:不带斜杠location /blog/ { proxy_pass http://127.0.0.1:3000;}# 访问 /blog/post/1 → 后端收到 /blog/post/1(完整路径原样传递)
# 情况 B:带斜杠location /blog/ { proxy_pass http://127.0.0.1:3000/;}# 访问 /blog/post/1 → 后端收到 /post/1(去掉 /blog 前缀再传递)一个斜杠,决定了 URL 前缀要不要剥掉。后端路由怎么都匹配不上、页面能打开但静态资源全是 404,八成就是这个问题。记不住规则没关系,改完在浏览器里试一下,观察后端日志实际收到的路径。
坑三:静态文件 403 Forbidden
文件明明在,路径明明对,ls 都能看到,浏览器就是 403。九成是 Linux 权限问题,Nginx 的工作进程是用普通用户(一般是 nginx 或 www-data)跑的,它得一路都有权限才能读到文件,任何一级目录缺执行权限都不行。
排查命令:
# 1. 看看 Nginx 工作进程用哪个用户跑的ps aux | grep nginx
# 2. 看 Nginx 配置里 user 指令grep "^user" /etc/nginx/nginx.conf
# 3. 确认目录每一级都有执行权限(x),最省事的修法:# 目录给 755,文件给 644,属主改成 Nginx 用户或加同组chmod 755 /var/www/blogchmod 644 /var/www/blog/index.htmlchown -R www-data:www-data /var/www/blog另外一个容易忽略的:SELinux(CentOS)。权限明明都对还是 403,setenforce 0 试一下如果立刻好了,就是 SELinux 的锅,再用 chcon 给目录改安全上下文做正式修复。
写在最后
个人建站的 Nginx 配置,说穿了就是这几件事:server 块定位网站、proxy_pass 代理动态服务、443 加证书、防盗链守住流量。搞懂这些,一个博客站的 Nginx 部分就齐活了。至于负载均衡、限流、缓存那些大厂玩意,等哪天真有那泼天的流量再说🐶。
教程是折腾后靠回忆写的,可能会有说得不对的地方,仅供参考。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
沪公网安备 31011502407501号