nginx配置
VPS 206.237.26.250 — nginx 配置详解
生成时间:2026-10-28
nginx 版本:nginx/1.28.3 (Ubuntu)
站点:Typecho 博客 https://ccl.leimu.cyou
证书:Let's Encrypt(CN=ccl.leimu.cyou,有效期至 2027-01-25)
一、文件结构
/etc/nginx/
├── nginx.conf 主配置(全局参数)
├── snippets/
│ ├── fastcgi-php.conf PHP 转发片段
│ └── snakeoil.conf (未使用)
├── sites-available/ 所有可用配置
│ ├── blog ← 名字叫 blog,其实是 Typecho 的配置
│ └── default-443 兜底块(IP 直连处理)
└── sites-enabled/ ← 只有放软链到这里才会生效
├── blog → ../sites-available/blog
└── default-443 → ../sites-available/default-443规则:nginx 只加载 sites-enabled/ 里的文件,sites-available/ 是仓库。
改完配置必须先 nginx -t 测试,再 systemctl reload nginx 生效。
二、主配置 nginx.conf
user www-data; # 工作进程用户,决定它能读哪些文件
worker_processes auto; # 自动 = CPU 核数,你这台是 1 核 → 1 个进程
worker_cpu_affinity auto; # 进程绑核,减少上下文切换
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768; # 单进程最大并发连接 = 768(1核机器够用)}
http {
sendfile on; # 零拷贝发文件,静态资源更快
tcp_nopush on; # 攒满一个包再发,减少小包
types_hash_max_size 2048;
server_tokens build; # 错误页不显示具体版本号(安全实践)
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1.2 TLSv1.3; # 禁用老旧的 SSLv3 / TLS1.0 / 1.1
ssl_prefer_server_ciphers off; # 优先用客户端的 cipher 顺序
access_log /var/log/nginx/access.log;
gzip on; # 压缩 HTML/CSS/JS,省带宽
include /etc/nginx/conf.d/*.conf; # conf.d/ 目前是空的
include /etc/nginx/sites-enabled/*; # ← 站点配置从这里进来}
关键点
指令
说明
worker_processes auto
1 核机器只开 1 个工作进程,开多了反而抢资源
worker_connections 768
理论最大并发 ≈ 768(还要减去反向代理的占用)
gzip on
文本类资源压缩,你这台的页面体积会小很多
ssl_protocols TLSv1.2 TLSv1.3
老协议有安全漏洞,已全部禁用
三、站点一:sites-available/blog(Typecho 主站)
⚠️ 注意:这个文件叫 blog,但内容是 Typecho 的配置,是历史遗留命名,容易误导。
3.1 HTTPS 主块(443)
server {
server_name ccl.leimu.cyou; # ① 只认这个域名
root /var/www/typecho; # ② 网站根目录
index index.php index.html; # ③ 默认首页文件
if ($request_uri ~* "^/index\.php/(?!action/)(.+)") { # ④ 旧链跳转
return 301 /$1$is_args$args;
}
location / { # ⑤ 伪静态路由
try_files $uri $uri/ /index.php$uri$is_args$args;
}
location ~ ^(.+\.php)(/.*)?$ { # ⑥ PHP 转发
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.5-fpm.sock;
}
location ~ /\. { # ⑦ 屏蔽隐藏文件
deny all;
}
listen 443 ssl; # ← 以下 5 行由 Certbot 管理,不要手改
listen [::]:443 ssl ipv6only=on;
ssl_certificate /etc/letsencrypt/live/ccl.leimu.cyou/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ccl.leimu.cyou/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;}
逐条解释
① server_name ccl.leimu.cyou
nginx 靠请求头里的 Host 匹配 server 块。只有访问这个域名才进这个块,其它一律走兜底块。
② root /var/www/typecho
网站根目录。文章不是真实文件,但主题用的 CSS/JS、上传的图片是真实文件,直接从这里读。
③ index index.php index.html
访问目录时默认找的文件。因为有 index.php,访问 / 会交给 PHP。
④ 旧链 301 跳转(这是踩过坑的地方)
if ($request_uri ~* "^/index.php/(?!action/)(.+)") {
return 301 /$1$is_args$args;}
- 作用:把 /index.php/archives/3/ 永久重定向到 /archives/3/
- (?!action/) 是负向前瞻,排除 /index.php/action/...。因为 Typecho 源码把登录表单的提交地址硬编码成带 index.php 的(Options.php 里的 ___loginAction()),如果它也 301,浏览器会把 POST 转成 GET,账号密码全丢 → 登录失败
- 用 $request_uri(原始未改写的 URI)而不是 $uri,避免内部重定向后重复触发死循环
- $1 捕获的内容不含开头斜杠,所以写成 /$1,否则会被拼成相对路径
⑤ 伪静态路由
try_files $uri $uri/ /index.php$uri$is_args$args;
按顺序尝试: - $uri —— 磁盘上有这个真实文件吗?(CSS、JS、图片走这条)
- $uri/ —— 有这个目录吗?
- /index.php$uri$is_args$args —— 都没有,就把路径当作 PATH_INFO 交给 PHP
这就是"去掉 index.php"的核心:地址栏干净,但底层还是同一个 PHP 在跑。
⑥ PHP 转发
location ~ ^(.+.php)(/.*)?$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.5-fpm.sock;
} - 正则 ^(.+.php)(/.*)?$ 能匹配 /index.php 和 /index.php/xxx/(原来的 \.php$ 匹配不了带路径的,是常见坑)
fastcgi_pass unix: 用 unix socket 而不是 TCP 端口,本机通信更快、更安全
snippets/fastcgi-php.conf 做的事:
fastcgi_split_path_info ^(.+?.php)(/.*)$; # 拆出 /archives/3/ 这段路径
try_files $fastcgi_script_name =404; # PHP 文件不存在直接 404,防解析漏洞
set $path_info $fastcgi_path_info; # 绕过 try_files 清空变量的 bug
fastcgi_param PATH_INFO $path_info; # 传给 PHP,Typecho 靠它路由
fastcgi_index index.php;
include fastcgi.conf; # 其它 FastCGI 参数
⑦ 屏蔽隐藏文件
location ~ /. {
deny all;
}
任何以 . 开头的文件/目录一律 403。实测日志里每天都有扫描器在试探 /.env、/.git/config、/.DS_Store、/vscode/sftp.json,全部被这条拦掉了。
3.2 HTTP 跳 HTTPS 块(80)
server {
if ($host = ccl.leimu.cyou) {return 301 https://$host$request_uri; # 域名访问 → 强制跳 HTTPS}
listen 80;
listen [::]:80;
server_name ccl.leimu.cyou;
return 404; # 兜底:不匹配就 404
}
这是 Certbot 生成的标准写法。if ($host = ...) 那一层是为了让 ACME 验证请求(HTTP-01)不被跳转干扰。
四、站点二:sites-available/default-443(兜底块)
server {
listen 443 ssl default_server; # default_server = 没有其它块匹配时的默认
listen [::]:443 ssl default_server;
server_name _;
ssl_reject_handshake on; # 直接拒绝 TLS 握手
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://ccl.leimu.cyou$request_uri; # IP 访问 HTTP → 跳域名}
为什么要这两个块
问题背景:证书只签给了 ccl.leimu.cyou,不含 IP。有人直接访问 https://206.237.26.250 时,浏览器发现"证书名字是域名,但你访问的是 IP",就会弹红色警告页,看起来像你的站有问题。
解决方案:
- ssl_reject_handshake on —— nginx 1.19.4+ 的特性,在 TLS 握手阶段就直接断开,浏览器的证书警告页都不会出现(比返回一张自签证书更干净)
- 80 端口的 IP 访问 → 301 跳到正式域名
关于 default_server
同一个「IP:端口」只能有一个 default_server。你的配置里: - 443 的 default_server 在 default-443
- 80 的 default_server 在 default-443
- blog 里两个 listen 都没有 default_server(之前手动去掉过,否则会报重复定义的错)
五、SSL 参数(Certbot 管理)
/etc/letsencrypt/options-ssl-nginx.conf:
ssl_session_cache shared:le_nginx_SSL:10m; # 会话复用缓存,减少握手开销
ssl_session_timeout 1440m; # 会话缓存 24 小时
ssl_session_tickets off; # 关闭 tickets(前向安全性更好)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...";
密码套件只包含 ECDHE(前向安全)和 GCM/CHACHA20(AEAD),安全性评分为 A 级。
证书信息:
subject = CN=ccl.leimu.cyou
issuer = Let's Encrypt (YE1)
到期 = 2027-01-25 16:28:24 GMT
续期 = certbot.timer(systemd 定时器,每天随机时间检查)
六、请求完整链路示例
以访问 https://ccl.leimu.cyou/archives/3/ 为例: - DNS 解析 ccl.leimu.cyou → 206.237.26.250
- TCP 443 握手 + TLS 握手(出示域名证书)
- Host 头 = ccl.leimu.cyou → 匹配 blog 的 443 server 块
- 检查 $request_uri 是否含 /index.php/ → 不含,跳过 301
location / → try_files
- /var/www/typecho/archives/3/ 不存在
- /var/www/typecho/archives/3/index.php 不存在
- 回退到 /index.php/archives/3/
- 重新匹配 location ~ ^(.+.php)(/.*)?$ → 命中
- fastcgi_pass → unix:/run/php/php8.5-fpm.sock
- PHP-FPM 执行 index.php,PATH_INFO = /archives/3/
- Typecho 查 SQLite → 找到文章 ID 3 → 套主题输出 HTML
nginx 收到响应 → gzip 压缩 → 返回浏览器
七、常用运维命令改配置后必做
nginx -t # 语法测试(一定要先跑)
systemctl reload nginx # 平滑重载(不断连接)
systemctl restart nginx # 硬重启(会断开现有连接)
查看状态
systemctl status nginx
ss -tlnp | grep -E ':(80|443)' # 看端口谁在占
日志
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
证书
certbot certificates # 查看所有证书
certbot renew --dry-run # 模拟续期(验证配置是否正确)
systemctl status certbot.timer # 查看自动续期定时器
回滚(本次改动的备份都在服务器)
ls /etc/nginx/sites-available/blog.bak.* # nginx 配置备份
ls /var/www/typecho/usr/typecho.db.bak.* # 数据库备份
八、已知问题与改进建议
项
现状
建议
配置文件命名
sites-available/blog 装的是 Typecho 配置
改名为 typecho 避免混淆
443 端口归属
nginx 占用,xray 已停止
若以后要启 x-ui,需做 SNI 分流(nginx 已编译 stream_ssl_preread 模块,可行)
Hugo 残留
/var/www/blog 还在(约 1.5MB)
无入口访问,可留可删
缓存策略
CSS/JS 未设 expires
可加静态资源缓存头,省带宽
HTTP/2
未启用
加 http2 on;(nginx 1.28 语法)可提升加载速度
安全头
无
可加 X-Content-Type-Options、Referrer-Policy 等
如需实施任一项,可直接在服务器上操作。