Nginx配置上传文件大小限制,client_max_body_size
前言
用户上传大文件,提示 413 Request Entity Too Large,这就是 Nginx 默认上传文件大小限制太小了。本文讲一下怎么改上传大小限制。
报错是什么样的
413 Request Entity Too Large
Nginx 默认限制是 1MB,传个大点的文件就报错。
配置方法
第一步:改 Nginx 配置
在 http 或 server 块里加:
client_max_body_size 50m;
意思是:最大允许上传 50MB 的文件。
第二步:同时改 PHP 配置
光改 Nginx 不够,PHP 自己也有上传限制:
upload_max_filesize = 50M
post_max_size = 50M
完整配置示例
Nginx 配置:
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html;
# 上传文件大小限制
client_max_body_size 50m;
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
PHP 配置(php.ini):
upload_max_filesize = 50M
post_max_size = 50M
重载生效
nginx -t
nginx -s reload
systemctl restart php-fpm
常见坑
坑1:只改了 Nginx,PHP 没改
改了 Nginx 的 client_max_body_size,但 PHP 的 upload_max_filesize 还是 2M,传大点的文件还是报错。
解决: Nginx 和 PHP 两边都要改。
坑2:413 还是 413
改了配置重载了,但还是 413。
原因: 改的不是实际加载的配置文件。
解决: 用 nginx -t 确认一下配置文件路径。
坑3:上传大文件超时
传个 50MB 的文件,传到一半 504 超时了。
原因: 上传慢,超时了。
解决: 调大超时时间:
client_body_timeout 300s;
fastcgi_read_timeout 300s;
坑4:post_max_size 比 upload_max_filesize 小
PHP 里 post_max_size 必须比 upload_max_filesize 大,否则还是上传不了。
建议: 两个设成一样大。
推荐配置
一般网站传图片、文档,设 20MB 够了:
client_max_body_size 20m;
upload_max_filesize = 20M
post_max_size = 20M
如果是视频网站,设 100MB 或更大。
总结
上传文件限制要改三个地方:
- Nginx 的
client_max_body_size - PHP 的
upload_max_filesize - PHP 的
post_max_size
记住: 改完两边都要重载,别只改一边。
遇到问题加QQ23979811 协助处理
Nginx负载均衡配置,upstream多台服务器
前言
网站访问量上来了,一台服务器扛不住,想用多台服务器一起跑。Nginx 的负载均衡功能就是干这个的:前面一台 Nginx 当入口,后面多台服务器一起干活。
什么是负载均衡
用户请求 → Nginx(负载均衡)→ 服务器A
→ 服务器B
→ 服务器C
Nginx 把请求按规则分发到不同的后端服务器,分担压力。
基础配置
第一步:定义 upstream
在 http 块里定义后端服务器组:
upstream backend {
server 192.168.1.101:80;
server 192.168.1.102:80;
server 192.168.1.103:80;
}
第二步:反向代理到后端
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
负载均衡策略
轮询(默认)
默认就是轮询,一个一个轮流来。
upstream backend {
server 192.168.1.101;
server 192.168.1.102;
}
加权轮询
服务器性能不一样,性能好的多分点请求:
upstream backend {
server 192.168.1.101 weight=3;
server 192.168.1.102 weight=1;
}
意思是:每4个请求,3个给101,1个给102。
ip_hash
同一个用户的请求永远打到同一台服务器(解决 session 问题):
upstream backend {
ip_hash;
server 192.168.1.101;
server 192.168.1.102;
}
健康检查
某台服务器挂了,Nginx 自动把它踢出去:
upstream backend {
server 192.168.1.101 max_fails=3 fail_timeout=30s;
server 192.168.1.102 max_fails=3 fail_timeout=30s;
}
意思是:3次失败就暂停30秒不发请求给它。
完整配置示例
http {
upstream backend {
server 192.168.1.101 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.102 weight=1 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
常见坑
坑1:session 不一致
用户登录了,刷新一下又掉登录。
原因: 轮询把请求分到不同服务器,session 没共享。
解决: 用 ip_hash,或者用 Redis 统一存 session。
坑2:后端服务器改了但 Nginx 没生效
加了一台新服务器,配置也写了,但没重载。
解决: nginx -s reload。
坑3:后端服务端口不对
后端跑在 8080 端口,upstream 里写的 80。
解决: upstream 里写对端口。
坑4:后端获取不到真实 IP
后端日志里记的都是 Nginx 的 IP。
解决: 加 X-Forwarded-For 头:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
总结
负载均衡就两步:
- upstream 定义后端服务器列表
- proxy_pass 转发到 upstream
记住: 多台服务器用负载均衡,记得处理 session 共享问题。
遇到问题加QQ23979811 协助处理
Nginx报504 Gateway Timeout超时排查
前言
访问网站提示 504 Gateway Timeout,意思是 Nginx 把请求转发给后端(PHP-FPM 或其他服务),后端半天没响应,Nginx 等不及了就返回 504。本文讲一下 504 的排查思路。
504 和 502 的区别
- 502 Bad Gateway:后端服务直接挂了,连不上
- 504 Gateway Timeout:后端服务在跑,但响应太慢,超时了
常见原因
原因1:PHP 程序执行太慢
一个 PHP 请求要跑 30 秒,Nginx 默认等 60 秒就超时了。
解决: 优化 PHP 代码,或者调大 Nginx 超时时间。
原因2:PHP-FPM 进程不够
PHP-FPM 进程数用完了,新请求排队等着。
解决: 增加 PHP-FPM 进程数。
原因3:后端服务挂了
后端的 Java/Node.js 服务没启动,或者端口不对。
解决: 确认后端服务在跑。
排查步骤
第一步:看 Nginx 错误日志
tail -f /var/log/nginx/error.log
找具体是哪个请求超时了。
第二步:看 PHP-FPM 状态
ps aux | grep php-fpm
看进程数是不是满了。
第三步:直接访问后端
绕过 Nginx,直接访问后端服务:
curl http://127.0.0.1:9000/
看后端自己响应快不快。
调大 Nginx 超时时间
如果程序确实需要跑很久,调大超时时间:
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 超时时间
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
}
参数说明:
| 参数 | 说明 |
|---|---|
fastcgi_connect_timeout |
连接后端超时时间 |
fastcgi_send_timeout |
发送请求超时时间 |
fastcgi_read_timeout |
读响应超时时间 |
增加 PHP-FPM 进程数
PHP-FPM 配置文件一般在 /etc/php-fpm.d/www.conf:
pm = static
pm.max_children = 50
改成 50 个进程,重载 PHP-FPM。
常见坑
坑1:只调了 Nginx 超时
调了 Nginx 超时,但 PHP 自己也有超时(max_execution_time)。
解决: 同时改 php.ini:
max_execution_time = 60
坑2:后端服务重启了但 Nginx 没重载
后端服务换了端口,Nginx 还连旧端口。
解决: 改完配置 nginx -s reload。
坑3:大文件上传超时
上传大文件,跑到一半 504。
原因: 上传慢,超时了。
解决: 调大 client_body_timeout 和 fastcgi_read_timeout。
坑4:CDN 也有超时
用了 CDN,CDN 自己也有超时限制。
解决: CDN 那边也调大超时时间。
总结
504 排查三步:
- 看 Nginx 错误日志
- 确认后端服务在跑
- 程序优化 or 调大超时时间
记住: 504 是后端响应太慢,不是 Nginx 本身的问题。
遇到问题加QQ23979811 协助处理
Nginx配置访问日志,日志格式详解
前言
网站出问题了,想查一下谁访问过哪个页面,怎么查?Nginx 的访问日志就记录了所有请求。本文讲一下 Nginx 怎么配置访问日志,日志格式都有什么字段。
默认日志位置
Nginx 默认访问日志在:
/var/log/nginx/access.log
错误日志在:
/var/log/nginx/error.log
查看实时日志
tail -f /var/log/nginx/access.log
日志格式定义
Nginx 自带一个默认格式叫 combined:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
常用日志变量
| 变量 | 说明 |
|---|---|
$remote_addr |
客户端 IP |
$time_local |
本地时间 |
$request |
请求行(方法+URL+协议) |
$status |
状态码 |
$body_bytes_sent |
发送字节数 |
$http_referer |
来源页面 |
$http_user_agent |
浏览器 UA |
自定义日志格式
想多记点信息,比如响应时间、X-Forwarded-For:
log_format myformat '$remote_addr - [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct="$upstream_connect_time"';
access_log /var/log/nginx/access.log myformat;
单独给某个站点配日志
多站点的时候,每个站点单独记日志:
server {
listen 80;
server_name www.aaa.com;
root /var/www/aaa;
index index.html;
access_log /var/log/nginx/aaa_access.log;
error_log /var/log/nginx/aaa_error.log;
}
关闭日志
不想记日志(不推荐):
access_log off;
常见坑
坑1:用了 CDN,日志里都是 CDN IP
访问日志里记的都是 CDN 的 IP,不是真实用户的。
原因: 请求经过 CDN 转发了。
解决: 记 X-Forwarded-For:
log_format combined '$http_x_forwarded_for - $remote_user [$time_local] ...';
坑2:日志太大占磁盘
访问日志一直写,把磁盘占满了。
解决: 配 logrotate 自动切割日志。
坑3:查不到某个 IP 的访问记录
想查某个 IP 今天访问了多少次,日志里没有。
原因: 日志已经被切割轮转了。
解决: 看昨天的日志文件:
grep "IP地址" /var/log/nginx/access.log.1
坑4:日志权限不对
Nginx 写不了日志。
解决: 确保日志目录属主是 www:
chown -R www:www /var/log/nginx/
常用日志分析命令
统计访问量前10的IP:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -10
统计 404 最多的页面:
awk '($9 == 404)' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -nr
总结
一行配置开启日志:
access_log /var/log/nginx/access.log;
记住: 日志是排查问题的第一手资料,出事先看日志。
遇到问题加QQ23979811 协助处理
Nginx限制请求速率,防止CC攻击配置
前言
网站被 CC 攻击,一个人疯狂刷页面,服务器直接卡死。Nginx 自带请求限速功能,能防止这种恶意刷接口、刷页面的行为。
什么是 CC 攻击
CC 攻击就是模拟正常用户,疯狂访问网站,把服务器资源耗尽,正常用户打不开网站。
Nginx 限速配置
用 limit_req_zone 定义限速规则,用 limit_req 应用到具体位置。
第一步:定义限速规则
在 http 块里(一般在 nginx.conf 或 conf.d 目录的配置文件里):
# 定义一个叫 "allips" 的区域,按 IP 限速,每秒允许 10 个请求
limit_req_zone $binary_remote_addr zone=allips:10m rate=10r/s;
解释:
$binary_remote_addr:按客户端 IP 限速zone=allips:10m:定义一个叫 allips 的内存区域,10M 大小rate=10r/s:每秒允许 10 个请求
第二步:在具体位置应用
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html;
# 应用限速
limit_req zone=allips burst=20 nodelay;
location / {
try_files $uri $uri/ =404;
}
}
burst=20 意思是:允许突发 20 个请求排队,超过就拒绝。
完整配置示例
http {
# 定义限速区域
limit_req_zone $binary_remote_addr zone=allips:10m rate=10r/s;
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html;
# 应用限速
limit_req zone=allips burst=20 nodelay;
location / {
try_files $uri $uri/ =404;
}
}
}
常用限速参数
| 参数 | 说明 |
|---|---|
rate=10r/s |
每秒 10 个请求 |
rate=30r/m |
每分钟 30 个请求 |
burst=20 |
允许 20 个突发请求排队 |
nodelay |
突发请求不延迟,立即处理 |
只限制某个接口
不想全站限速,只想限制某个接口?
location /api/login {
limit_req zone=allips burst=5 nodelay;
}
这样只有登录接口限速,其他接口不限。
常见坑
坑1:限速太严,正常用户也被拦了
设了 rate=1r/s,用户点一下按钮就 503。
解决: 把速率调宽一点,比如 rate=10r/s,burst=20。
坑2:用了 CDN,限速按 CDN IP 算了
用了 CDN,所有请求都从 CDN 过来,Nginx 看到的都是 CDN 的 IP,把 CDN 给限了。
解决: 用真实客户端 IP 来限速:
set_real_ip_from CDN的IP段;
real_ip_header X-Forwarded-For;
limit_req_zone $realip zone=allips:10m rate=10r/s;
坑3:报错 503 Too Many Requests
用户访问提示 503。
原因: 请求太快,被限速了。
解决: 调大 burst 和 rate。
坑4:limit_req_zone 写在 server 块里
把 limit_req_zone 写在 server 块里了,Nginx 报错。
原因: limit_req_zone 只能写在 http 块里。
解决: 挪到 nginx.conf 的 http 块里。
总结
防 CC 攻击就这三步:
- http 块里定义
limit_req_zone - server 块里加
limit_req nginx -s reload重载
记住: 限速别太严,正常用户别给拦了。
遇到问题加QQ23979811 协助处理
Nginx设置默认首页,index.html配置
前言
访问网站根目录,Nginx 不知道默认打开哪个文件,就返回 403 或者 404。这是因为没配置默认首页。本文讲一下 Nginx 怎么设置默认首页。
什么是默认首页
用户访问 www.example.com/,Nginx 自动找目录下的 index.html 打开,不用手动输入文件名。
配置方法
用 index 指令设置:
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.html;
}
多个默认首页
Nginx 会按顺序找,找到第一个存在的就用:
index index.html index.htm index.php;
意思是:先找 index.html,没有就找 index.htm,再没有就找 index.php。
完整站点配置
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
自定义默认首页
不想用 index.html,想用别的文件名?
index default.html;
这样访问根目录就打开 default.html。
常见坑
坑1:访问根目录 403 Forbidden
配置了 root,也放了 index.html,但访问还是 403。
原因1: 文件名写错了,比如是 Index.html(大写 I)。
解决: Linux 区分大小写,文件名必须和 index 配置一致。
原因2: 文件权限不对。
解决: 确保文件权限是 644,目录是 755。
坑2:默认首页不生效
改了 index 配置,但还是打开旧的首页。
原因: 没重载 Nginx。
解决:
nginx -s reload
坑3:PHP 首页不解析
默认首页设成 index.php,但访问直接下载 PHP 文件。
原因: PHP 解析配置没写对。
解决: 确保有 PHP 的 location 配置:
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
坑4:静态首页和 PHP 首页顺序
index 里先写 index.html 还是 index.php?
建议: 把常用的放前面。如果主要是 PHP 程序,就把 index.php 放前面。
总结
一行配置搞定:
index index.php index.html index.htm;
记住: index 后面跟多个文件名,Nginx 按顺序找,找到第一个存在的就用。
遇到问题加QQ23979811 协助处理
Nginx配置gzip压缩,提升网站加载速度
前言
网站打开慢?Nginx 开个 gzip 压缩,能把传输体积压缩 70% 以上,用户打开快很多,还省带宽。对 SEO 也有帮助。
什么是 gzip 压缩
gzip 是一种压缩算法。Nginx 把 HTML、CSS、JS 等文本文件压缩后再发给浏览器,浏览器收到后自动解压。
比如一个 100KB 的 CSS 文件,压缩后可能只有 20KB。
配置方法
在 Nginx 配置文件里加 gzip 相关配置。
基础配置
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
完整示例
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.html;
# gzip 压缩
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
}
参数说明
| 参数 | 说明 |
|---|---|
gzip on |
开启 gzip |
gzip_min_length 1k |
小于 1KB 的文件不压缩 |
gzip_comp_level 6 |
压缩级别 1-9,6 最合适 |
gzip_types |
压缩哪些类型的文件 |
gzip_vary on |
给响应头加 Vary: Accept-Encoding |
测试是否生效
方法一:浏览器 F12
打开浏览器开发者工具 → Network → 点一个 CSS/JS 文件 → 看 Response Headers 里有没有:
Content-Encoding: gzip
有就说明生效了。
方法二:curl 命令
curl -I -H "Accept-Encoding: gzip" https://www.example.com/style.css
看返回头里有没有 Content-Encoding: gzip。
常见坑
坑1:图片也压缩
配置里把图片类型也加上了。
问题: 图片(jpg/png)本身已经是压缩过的格式,再 gzip 没意义,还浪费 CPU。
解决: 只压缩文本文件,图片不用压。
坑2:压缩级别太高
设成了 9,以为压缩越狠越好。
问题: 级别太高会占用更多 CPU,压缩时间长,反而拖慢响应。
解决: 用 6 就够了,性价比最高。
坑3:PHP 页面没压缩
HTML 压缩了,但 PHP 输出的页面没压缩。
原因: PHP 页面已经被 PHP-FPM 压缩过了,Nginx 不会再压。
解决: 不用管,PHP 那边自己处理就行。
坑4:CDN 后面 gzip 不生效
用了 CDN,回源开了 gzip,但用户那边没压。
原因: CDN 回源拿的是压缩后的文件,CDN 缓存了。
解决: 刷新 CDN 缓存,或者 CDN 那边也开 gzip。
总结
开 gzip 就这几行配置:
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript;
gzip_vary on;
记住: 文本文件压,图片别压;压缩级别用 6 就行。
遇到问题加QQ23979811 协助处理
Nginx报404 Not Found原因,文件路径检查
前言
访问网站出现 404 Not Found,意思是 Nginx 找不到文件。大部分情况是路径配置不对。本文讲一下 404 的常见原因和排查方法。
404 的常见原因
原因1:root 路径不对
Nginx 配置里的 root 指向的目录不对,文件不在那个目录里。
检查:
server {
root /var/www/html; # 确认这个路径
}
去服务器上看一下:
ls /var/www/html/
看 index.html 在不在。
原因2:index 首页没配置
访问根目录,Nginx 不知道默认打开哪个文件。
解决: 配置 index:
server {
root /var/www/html;
index index.php index.html index.htm;
}
原因3:伪静态没配好
ThinkPHP、WordPress 这种程序,需要伪静态规则。没配的话,访问文章页就 404。
解决: 加上伪静态规则:
location / {
try_files $uri $uri/ /index.php?$args;
}
原因4:文件权限不对
文件存在,但 Nginx 读不了。
检查:
ls -la /var/www/html/
确保文件权限是 644,目录是 755,属主是 www。
排查步骤
第一步:确认文件真的存在
ls -la /var/www/html/index.html
第二步:确认 Nginx 配置的 root 路径
grep root /etc/nginx/conf.d/你的站点.conf
第三步:看 Nginx 错误日志
tail -f /var/log/nginx/error.log
访问一下 404 的页面,看日志里写的什么。
常见坑
坑1:root 和 alias 搞混了
很多人分不清 root 和 alias。
区别:
root:把请求路径拼到 root 后面alias:把请求路径替换成 alias 后面的路径
举个例子:
请求 /a/test.html
# root 方式
root /var/www;
# 实际找 /var/www/a/test.html
# alias 方式
location /a/ {
alias /var/www/;
}
# 实际找 /var/www/test.html
坑2:首页能访问,内页 404
首页打开正常,点文章就 404。
原因: 伪静态没配。
解决: 给程序配上伪静态规则。
坑3:PHP 文件 404
访问 PHP 文件,直接下载或者 404。
原因: PHP 解析没配置。
解决: 确保有 PHP 的 location 配置:
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
坑4:改了文件名但浏览器还缓存
本地改了文件名,访问还是 404。
原因: 浏览器缓存了旧的 404 响应。
解决: 清浏览器缓存,或者用无痕模式测试。
总结
404 排查三步走:
- 文件在不在?
- root 路径对不对?
- 伪静态配没配?
记住: 排查 404 先看错误日志,比瞎猜快多了。
遇到问题加QQ23979811 协助处理
Nginx重启、重载、停止命令,nginx -s reload
前言
改了 Nginx 配置,怎么让它生效?很多新手直接重启服务器,其实没必要。Nginx 有专门的重载命令,不中断服务。
常用命令汇总
| 操作 | 命令 |
|---|---|
| 重载配置(不中断) | nginx -s reload |
| 停止 Nginx | nginx -s stop |
| 优雅停止 | nginx -s quit |
| 检查配置 | nginx -t |
| 重启 Nginx | systemctl restart nginx |
| 查看状态 | systemctl status nginx |
重载配置(最常用)
改完配置,用这个:
nginx -s reload
好处: 不中断现有连接,新连接用新配置。生产环境用这个,不要直接重启。
检查配置
改完配置先检查一下语法:
nginx -t
输出 syntax is ok 才能重载。不然重载失败 Nginx 就挂了。
停止 Nginx
快速停止
nginx -s stop
直接杀进程,不管正在处理的请求。
优雅停止
nginx -s quit
等正在处理的请求完成后再停止。生产环境用这个。
重启 Nginx
用 systemctl 管理的:
systemctl restart nginx
编译安装的:
nginx -s stop
nginx
查看 Nginx 状态
systemctl status nginx
或者看进程:
ps aux | grep nginx
开机自启
systemctl enable nginx
常见坑
坑1:改了配置不生效
执行了 nginx -s reload,但配置还是老的。
原因1: 没检查配置语法,reload 失败了。
解决: 先跑 nginx -t,确认配置没问题再 reload。
原因2: 改的不是实际加载的配置文件。
解决: 用 nginx -t 确认一下加载的是哪个配置文件。
坑2:reload 后 Nginx 挂了
改了配置,reload 后网站打不开。
原因: 配置写错了,新进程起不来,老进程也退了。
解决: 赶紧回滚配置,然后 systemctl restart nginx。
预防: 改配置前先备份,改完先 nginx -t。
坑3:找不到 nginx 命令
执行 nginx: command not found。
原因: 编译安装的 Nginx 不在 PATH 里。
解决: 用完整路径:
/usr/local/nginx/sbin/nginx -s reload
坑4:reload 提示 no PID
执行 nginx -s reload 提示 no PID found。
原因: Nginx 没启动。
解决: 先启动 Nginx。
总结
日常操作流程:
- 改配置
nginx -t检查语法nginx -s reload重载- 测试网站是否正常
记住: 生产环境改配置,永远用 reload,不要用 restart,不中断服务。
遇到问题加QQ23979811 协助处理
Nginx查看配置文件位置,nginx.conf在哪
前言
刚装完 Nginx,想改个配置,发现不知道 nginx.conf 在哪。不同安装方式,配置文件位置不一样。本文讲一下怎么找 Nginx 配置文件。
方法一:nginx -t 命令
最直接的方法:
nginx -t
输出会显示配置文件路径:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
第一行就是配置文件位置。
方法二:ps 命令看进程
看 Nginx 进程启动时用的哪个配置:
ps aux | grep nginx
或者:
ps -ef | grep nginx
输出里 master 进程后面的 -c 参数就是配置文件路径。
方法三:nginx -V 看编译参数
nginx -V
输出里 --conf-path= 后面就是默认配置文件路径。
常见安装方式的默认路径
yum/apt 安装(包管理器)
/etc/nginx/nginx.conf
子配置一般在:
/etc/nginx/conf.d/
/etc/nginx/sites-available/
编译安装
默认在:
/usr/local/nginx/conf/nginx.conf
或者:
/usr/local/nginx/conf/
Docker 安装
容器里的路径是:
/etc/nginx/nginx.conf
如果挂载了宿主机目录,就在宿主机对应目录里。
常用配置文件结构
/etc/nginx/
├── nginx.conf # 主配置文件
├── conf.d/ # 子配置目录
│ ├── aaa.conf
│ └── bbb.conf
├── fastcgi_params # FastCGI 参数
├── mime.types # MIME 类型
└── nginx.conf.default # 默认配置模板
怎么查看完整配置
看主配置
vi /etc/nginx/nginx.conf
看所有加载的配置
nginx -T
-T 会打印所有配置文件的内容,包括 include 进来的子配置。
常见坑
坑1:改了配置不生效
改了 nginx.conf,但重载后还是老配置。
原因: 你改的不是 Nginx 实际加载的配置文件。
解决: 用 nginx -t 确认一下到底加载的是哪个配置文件。
坑2:找不到 nginx.conf
执行 find / -name nginx.conf 找不到。
原因: 可能是编译安装的,不在标准路径。
解决: 用 nginx -V 看 --conf-path= 参数。
坑3:conf.d 里的配置没生效
在 conf.d 里加了 .conf 文件,但没生效。
原因1: 后缀不是 .conf
原因2: 主配置里没 include conf.d/*.conf
解决: 确认 nginx.conf 里有这一行:
include /etc/nginx/conf.d/*.conf;
总结
| 方法 | 命令 |
|---|---|
| 最推荐 | nginx -t |
| 看进程参数 | ps aux \| grep nginx |
| 看编译参数 | nginx -V |
| 看全部配置 | nginx -T |
记住: 找不到配置文件先跑 nginx -t,一目了然。
遇到问题加QQ23979811 协助处理
Nginx配置伪静态,ThinkPHP/WordPress规则
前言
ThinkPHP、WordPress 这些程序,URL 一般都带 index.php 或者 ? 参数,比如 www.example.com/index.php?m=user&a=login。伪静态就是把这种丑陋的 URL 变成好看的,比如 www.example.com/user/login.html。对 SEO 也友好。
什么是伪静态
伪静态就是 Nginx 把好看的 URL 内部重写到真实的 PHP 文件,用户看到的是好看的 URL,实际服务器还是走原来的程序。
通用伪静态规则
大部分 PHP 框架都用这个规则:
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
}
}
意思是:如果请求的文件/目录不存在,就重写到 index.php,把路径作为参数传过去。
ThinkPHP 伪静态规则
ThinkPHP 5/6 版本
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
}
}
完整站点配置:
server {
listen 80;
server_name www.example.com;
root /var/www/thinkphp/public;
index index.php index.html;
location / {
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=/$1 last;
}
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
WordPress 伪静态规则
WordPress 的伪静态叫"固定链接",设置成文章名(/%postname%/)就需要 Nginx 重写规则:
location / {
try_files $uri $uri/ /index.php?$args;
}
完整配置:
server {
listen 80;
server_name www.example.com;
root /var/www/wordpress;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
其他常见规则
去掉 index.php
把 /index.php/xxx 变成 /xxx:
rewrite ^/index.php/(.*)$ /$1 permanent;
伪静态成 .html 结尾
if (!-e $request_filename) {
rewrite ^/(.*)\.html$ /index.php?s=/$1 last;
}
检查配置并重载
nginx -t
nginx -s reload
常见坑
坑1:伪静态不生效
配置了伪静态,但 URL 还是 404。
原因1: 程序后台没开伪静态。
ThinkPHP 需要在后台开启"URL 重写"。WordPress 需要在后台设置固定链接。
原因2: Nginx 没重载。
原因3: 规则写错了,比如少了个 $ 或者 last 没加。
坑2:CSS/JS 加载不出来
配置伪静态后,样式全乱了。
原因: 静态文件也被重写到 index.php 了。
解决: 静态文件直接返回,不走重写:
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 30d;
}
坑3:后台登录跳转不对
WordPress 后台登录后,跳转不对。
原因: 域名配置不对,或者 WordPress 后台地址设置不对。
解决: 确认 WordPress 后台的"站点地址"和"WordPress 地址"都是正确的域名。
坑4:rewrite 和 try_files 冲突
两种写法不能混用:
# 这两种选一种,别都写
rewrite ^(.*)$ /index.php?s=/$1 last;
try_files $uri $uri/ /index.php?$args;
总结
| 框架 | 重写规则 |
|---|---|
| 通用 PHP 框架 | rewrite ^(.*)$ /index.php?s=$1 last; |
| ThinkPHP5/6 | rewrite ^(.*)$ /index.php?s=/$1 last; |
| WordPress | try_files $uri $uri/ /index.php?$args; |
提醒: 配完伪静态,记得在程序后台也开一下对应的开关,两边都要配。
遇到问题加QQ23979811 协助处理
Nginx配置多站点,一个服务器跑多个网站
前言
买了一台服务器,想跑好几个网站,不用买好几台服务器。Nginx 的虚拟主机(vhost)功能就是干这个的。本文讲一下怎么用 Nginx 配置多站点。
原理
Nginx 通过 server_name 来区分不同的网站。同一个 IP 同一个端口,根据访问的域名不同,返回不同的网站内容。
用户访问 www.aaa.com → 网站A的目录
用户访问 www.bbb.com → 网站B的目录
基础配置方法
第一步:准备网站目录
mkdir -p /var/www/aaa
mkdir -p /var/www/bbb
放两个测试首页:
echo "网站A" > /var/www/aaa/index.html
echo "网站B" > /var/www/bbb/index.html
第二步:创建 Nginx 配置
在 /etc/nginx/conf.d/ 目录下,每个网站一个配置文件。
网站A配置:
vi /etc/nginx/conf.d/aaa.conf
内容:
server {
listen 80;
server_name www.aaa.com aaa.com;
root /var/www/aaa;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
网站B配置:
vi /etc/nginx/conf.d/bbb.conf
内容:
server {
listen 80;
server_name www.bbb.com bbb.com;
root /var/www/bbb;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
第三步:检查配置并重载
nginx -t
nginx -s reload
第四步:解析域名
把 www.aaa.com 和 www.bbb.com 都解析到这台服务器的 IP 上。
这样两个网站就都能访问了。
带 PHP 的多站点
如果每个网站都要跑 PHP,每个站点的配置里加上 PHP 转发:
server {
listen 80;
server_name www.aaa.com;
root /var/www/aaa;
index index.php index.html;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
带 HTTPS 的多站点
每个站点单独配证书:
server {
listen 443 ssl http2;
server_name www.aaa.com;
ssl_certificate /etc/nginx/ssl/aaa.crt;
ssl_certificate_key /etc/nginx/ssl/aaa.key;
root /var/www/aaa;
index index.php index.html;
}
每个网站用自己的证书,互不影响。
目录结构建议
/etc/nginx/
├── nginx.conf
├── conf.d/
│ ├── aaa.conf # 网站A配置
│ ├── bbb.conf # 网站B配置
│ └── ccc.conf # 网站C配置
每个网站一个配置文件,方便管理。
常见坑
坑1:所有网站都显示同一个内容
访问不同域名,显示的都是同一个网站的内容。
原因: Nginx 默认 server 的问题。
解决: 把第一个 server 块设为默认:
server {
listen 80 default_server;
server_name _;
return 444;
}
其他网站用各自的 server_name。
坑2:新加的站点不生效
加了新的配置文件,但网站还是访问不了。
原因1: 配置文件后缀不对。Nginx 默认只加载 .conf 后缀的文件。
原因2: 没重载 Nginx。
解决:
nginx -t
nginx -s reload
坑3:PHP 解析不对
网站A的 PHP 请求跑到网站B去了。
原因: root 路径不对,或者 PHP 配置里的 root 写死了。
解决: 每个站点的 PHP location 里都写清楚自己的 root:
location ~ \.php$ {
root /var/www/aaa;
...
}
坑4:文件权限不对
网站B的文件 Nginx 读不了。
解决: 确保所有网站文件的属主都是 www:
chown -R www:www /var/www/
总结
| 步骤 | 操作 |
|---|---|
| 建目录 | mkdir /var/www/网站名 |
| 写配置 | 每个网站一个 .conf 文件 |
| server_name | 写域名 |
| root | 写网站目录 |
| 重载 | nginx -s reload |
| 解析域名 | 域名解析到服务器IP |
记住: 一个网站一个配置文件,别全写在 nginx.conf 里,不好管理。
遇到问题加QQ23979811 协助处理
Nginx限制IP访问,拉黑恶意爬虫IP配置
前言
网站被恶意爬虫狂刷,服务器CPU跑满?或者有人恶意攻击你的网站?可以用 Nginx 直接把这些 IP 拉黑,不让他们访问。本文讲一下怎么配置 IP 访问控制。
怎么找到恶意 IP
先看 Nginx 访问日志,找出访问最频繁的 IP:
# 统计访问次数最多的前 20 个 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
如果某个 IP 访问量特别大,可能就是恶意爬虫或者攻击。
拉黑单个 IP
在 Nginx 配置里加:
server {
listen 80;
server_name www.example.com;
root /var/www/html;
# 拉黑某个 IP
location / {
deny 192.168.1.100;
allow all;
}
}
被拉黑的 IP 访问会返回 403 Forbidden。
拉黑 IP 段
拉黑整个 C 段
deny 192.168.1.0/24;
就是拉黑 192.168.1.1 到 192.168.1.255。
拉黑多个 IP
deny 192.168.1.100;
deny 192.168.1.101;
deny 192.168.1.102;
白名单模式(只允许某些 IP 访问)
如果想只允许特定 IP 访问(比如后台管理页面):
# 只允许内网 IP 访问 /admin
location /admin {
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}
其他 IP 访问 /admin 都会返回 403。
用 include 单独管理黑名单
IP 多了不想写在主配置里,可以单独放一个文件:
第一步:创建黑名单文件
vi /etc/nginx/conf.d/blacklist.conf
内容:
deny 192.168.1.100;
deny 192.168.1.101;
deny 10.0.0.0/8;
第二步:在主配置里引入
server {
listen 80;
server_name www.example.com;
root /var/www/html;
# 引入黑名单
include /etc/nginx/conf.d/blacklist.conf;
location / {
try_files $uri $uri/ =404;
}
}
这样以后加黑名单只要改 blacklist.conf 文件,然后重载 Nginx 就行。
对某个目录单独限制
比如只对 /admin 目录限制:
location /admin {
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
root /var/www/html;
index index.php;
}
检查配置并重载
nginx -t
nginx -s reload
用防火墙拉黑(更彻底)
如果想在系统层面拉黑,用 firewall-cmd:
# 拉黑单个 IP
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='192.168.1.100' reject"
# 重新加载
firewall-cmd --reload
或者用 iptables:
iptables -I INPUT -s 192.168.1.100 -j DROP
常见坑
坑1:改了黑名单忘了 reload
加了黑名单,但 IP 还能访问。
原因: Nginx 没重载。
解决:
nginx -s reload
坑2:把自己 IP 拉黑了
测试的时候不小心把自己 IP 也拉黑了,自己也访问不了了。
解决: 先从服务器上用 curl 测试:
curl -I http://127.0.0.1
确认没问题再 reload。
坑3:CDN 后面拿不到真实 IP
用了 CDN 之后,日志里全是 CDN 的 IP,看不到用户真实 IP。
解决: 先配置 Nginx 获取真实 IP:
set_real_ip_from CDN的IP段;
real_ip_header X-Forwarded-For;
这样日志里才能看到用户真实 IP,才能正确拉黑。
总结
| 操作 | 命令/配置 |
|---|---|
| 拉黑单个 IP | deny 192.168.1.100; |
| 拉黑 IP 段 | deny 192.168.1.0/24; |
| 白名单模式 | allow 127.0.0.1; deny all; |
| 单独黑名单文件 | include /etc/nginx/conf.d/blacklist.conf; |
| 重载生效 | nginx -s reload |
提醒: 拉黑 IP 之前先确认是不是真的恶意,别误杀正常用户。
遇到问题加QQ23979811 协助处理
Nginx反向代理配置,代理后端PHP服务
前言
LNMP 架构里,Nginx 收到 PHP 请求之后,要转发给 PHP-FPM 处理。这个转发的配置就是 fastcgi_pass。本文讲一下完整的 Nginx 代理 PHP 服务配置。
两种代理方式
Nginx 代理后端服务有两种方式:
- proxy_pass:代理 HTTP 服务(比如 Node.js、Java、Python)
- fastcgi_pass:代理 FastCGI 服务(就是 PHP-FPM)
PHP 用的是 fastcgi_pass,不是 proxy_pass,别搞混了。
基础配置
完整 LNMP 配置示例
server {
listen 80;
server_name www.example.com;
root /var/www/html;
index index.php index.html;
# 访问根目录
location / {
try_files $uri $uri/ =404;
}
# PHP 请求转发给 PHP-FPM
location ~ \.php$ {
root /var/www/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
关键参数说明
| 参数 | 作用 |
|---|---|
fastcgi_pass 127.0.0.1:9000 |
PHP-FPM 监听的地址和端口 |
fastcgi_pass unix:/run/php-fpm/www.sock |
用 socket 文件方式(更高效) |
fastcgi_index index.php |
默认首页 |
SCRIPT_FILENAME |
PHP 文件的完整路径 |
include fastcgi_params |
引入其他参数 |
socket 方式 vs 端口方式
端口方式(TCP)
fastcgi_pass 127.0.0.1:9000;
优点:跨服务器可以用
缺点:多了一层 TCP 开销,稍微慢一点
Socket 方式(Unix Domain Socket)
fastcgi_pass unix:/run/php-fpm/www.sock;
优点:更快,少了 TCP 开销
缺点:只能在同一台服务器上用
生产环境推荐用 socket 方式,更快。
完整生产环境配置
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/www.crt;
ssl_certificate_key /etc/nginx/ssl/www.key;
root /var/www/html;
index index.php index.html;
# 日志
access_log /var/log/nginx/www.access.log;
error_log /var/log/nginx/www.error.log;
# 静态资源直接返回
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 30d;
}
# PHP 请求转发
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 超时设置
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
}
# 禁止访问 .ht 等文件
location ~ /\.ht {
deny all;
}
}
常见坑
坑1:SCRIPT_FILENAME 路径不对
配置了 SCRIPT_FILENAME /var/www/html$fastcgi_script_name;,但 root 不是这个路径。
原因: 写死了路径,换个目录就错了。
解决: 用 $document_root:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
坑2:socket 路径不一致
Nginx 配置里写的 socket 路径和 PHP-FPM 实际的不一致。
检查 PHP-FPM 的 socket 路径:
grep listen /etc/php-fpm.d/www.conf
确保两边路径一模一样。
坑3:PHP 不解析,直接下载
访问 index.php,浏览器直接下载了文件,没解析。
原因: Nginx 没把 .php 请求转发给 PHP-FPM。
检查: 配置里有没有 location ~ \.php$ 这一段,有没有 fastcgi_pass。
坑4:404 Not Found
访问 PHP 文件报 404。
原因: SCRIPT_FILENAME 路径不对,PHP-FPM 找不到文件。
解决: 确认 root 和 SCRIPT_FILENAME 配置正确。
检查配置并重载
nginx -t
nginx -s reload
总结
| 配置项 | 说明 |
|---|---|
fastcgi_pass |
PHP-FPM 地址(端口或socket) |
SCRIPT_FILENAME |
PHP 文件完整路径 |
fastcgi_params |
其他参数集合 |
expires |
静态资源缓存 |
| 超时设置 | fastcgi_connect_timeout 等 |
记住: PHP 用 fastcgi_pass,不是 proxy_pass,别搞混了。
遇到问题加QQ23979811 协助处理
Nginx 403 Forbidden权限不足原因与解决
前言
打开网站突然报 403 Forbidden,权限不足。这是 Nginx 最常见的报错之一。本文讲一下 403 的常见原因和解决办法。
403 是什么意思
403 Forbidden 表示服务器理解请求,但拒绝执行。说白了就是 Nginx 不让你访问这个文件/目录。
常见原因和解决办法
原因1:文件/目录权限不对
最常见的原因。Nginx 运行的用户(一般是 www)没有权限读文件。
检查权限:
ls -l /var/www/html/index.html
如果是 644 以下,或者属主是 root,Nginx 就读不了。
解决:
# 改文件权限
chmod 644 /var/www/html/index.html
# 改目录权限
chmod 755 /var/www/html
# 改属主
chown -R www:www /var/www/html
原因2:首页文件不存在
访问网站根目录,但没有 index 首页文件。
检查:
ls /var/www/html/
看看有没有 index.html 或 index.php。
解决:
- 把首页文件名改成 index.html 或 index.php
- 或者在 Nginx 配置里指定 index
index index.html index.php;
原因3:目录没有索引权限
访问一个目录,目录里没有首页文件,Nginx 又没开目录列表。
解决方法一: 放一个 index.html 进去
解决方法二: 开目录列表(不推荐,不安全)
location /uploads/ {
autoindex on;
}
原因4:SELinux 没关
CentOS7 默认开了 SELinux,会拦截 Nginx 访问文件。
检查 SELinux 状态:
getenforce
如果是 Enforcing,说明开着。
解决:
# 临时关闭
setenforce 0
# 永久关闭
vi /etc/selinux/config
# 改成 SELINUX=disabled
原因5:Nginx 配置了访问控制
Nginx 配置里限制了 IP 访问,或者需要密码。
检查配置:
grep -i "deny\|allow\|auth_basic" /etc/nginx/conf.d/*.conf
如果有 deny all 或者 IP 白名单,把你的 IP 加上或者去掉限制。
原因6:文件路径不对
Nginx 配置的 root 路径和实际文件路径不一致。
检查配置:
grep root /etc/nginx/conf.d/*.conf
确认 root 指向的目录里确实有你的文件。
排查步骤
- 先看错误日志
tail -f /var/log/nginx/error.log
日志里会写得很清楚是什么原因。
- 检查文件权限
ls -l /var/www/html/
- 检查 SELinux
getenforce
- 检查 Nginx 配置
nginx -t
常见坑
坑1:只改了文件权限没改目录权限
文件权限是 644,但上级目录权限不对,Nginx 还是进不去。
解决: 一级一级改目录权限:
chmod 755 /var
chmod 755 /var/www
chmod 755 /var/www/html
坑2:上传的文件是 root 创建的
用 root 上传了文件,文件属主是 root,Nginx 用户读不了。
解决:
chown -R www:www /var/www/html
坑3:SELinux 改了没重启
临时关了 SELinux,但重启服务器又开了。
解决: 改配置文件永久关闭,别只 setenforce 0。
坑4:用了 CDN 或 WAF
前面有 CDN 或 WAF,它们拦截了请求。
解决: 直接用服务器 IP 访问,看看是不是还是 403。如果直接 IP 访问没问题,就是 CDN/WAF 的问题。
总结
| 原因 | 解决办法 |
|---|---|
| 文件权限不对 | chmod 644 文件、chmod 755 目录 |
| 首页不存在 | 放个 index.html 进去 |
| SELinux 拦截 | 关闭 SELinux |
| IP 被限制 | 检查 Nginx allow/deny 配置 |
| 路径不对 | 检查 root 配置 |
| 属主不对 | chown www:www 目录 |
记住: 403 先看错误日志,大部分问题日志里都写得很清楚。
遇到问题加QQ23979811 协助处理
Nginx静态资源缓存配置,提升网站访问速度
前言
网站访问慢?很大一部分原因是静态资源(图片、CSS、JS)每次都要重新从服务器下载。配置浏览器缓存之后,用户第二次访问就直接从本地缓存读了,速度快很多。本文讲一下 Nginx 怎么配置静态资源缓存。
缓存原理
浏览器第一次访问网站,服务器返回静态资源的时候,加上 Cache-Control 头,告诉浏览器"这个文件你缓存多久"。
第二次访问的时候,浏览器发现本地有缓存,且还没过期,就直接用本地的,不用再请求服务器了。
配置方法
按文件类型设置缓存时间
在 Nginx 配置里加:
server {
listen 443 ssl http2;
server_name www.example.com;
root /var/www/html;
index index.html;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|swf|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
# HTML 文件不缓存
location ~* \.html$ {
expires -1;
}
}
缓存时间说明
| 文件类型 | 建议缓存时间 | 说明 |
|---|---|---|
| 图片(jpg/png/gif) | 30天 | 图片一般不怎么改 |
| CSS/JS | 7天 | 改了文件版本号就能更新 |
| HTML | 不缓存 | 保证用户看到最新页面 |
| 字体文件 | 30天 | 基本不变 |
更精细的配置
# 图片缓存 30 天
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# CSS JS 缓存 7 天
location ~* \.(css|js)$ {
expires 7d;
add_header Cache-Control "public";
}
# 字体文件缓存 30 天
location ~* \.(woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# HTML 不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
参数说明
| 参数 | 作用 |
|---|---|
expires 30d |
缓存30天 |
expires 1h |
缓存1小时 |
expires -1 |
不缓存 |
Cache-Control public |
允许所有缓存(浏览器、CDN) |
Cache-Control private |
只允许浏览器缓存 |
Cache-Control no-cache |
不缓存(每次都要验证) |
Cache-Control no-store |
完全不缓存 |
immutable |
文件永远不变(不用问服务器了) |
怎么验证缓存生效了
打开浏览器 F12 → Network → 点一个静态资源 → 看 Response Headers:
Cache-Control: max-age=2592000
Expires: xxx
有这两个头就说明缓存生效了。
常见坑
坑1:改了 CSS 但页面没更新
更新了 CSS 文件,但用户看到的还是老版本。
原因: 浏览器缓存了老 CSS。
解决:
- 改文件名:
style.css→style.v2.css - 或者加版本号:
style.css?v=20261007
这样浏览器就会当成新文件重新下载。
坑2:HTML 也被缓存了
页面内容更新了,但用户看到的还是老页面。
原因: HTML 文件也被缓存了。
解决: HTML 文件设置不缓存:
location ~* \.html$ {
expires -1;
}
坑3:缓存时间太长
设置了一年缓存,万一文件要改怎么办?
解决: 用文件指纹(版本号)的方式,比如:
app.3a7b2c.jsstyle.8d9e1f.css
文件内容变了,文件名就变了,缓存自然就失效了。
坑4:配置了缓存但不生效
加了 expires 但浏览器还是每次都请求。
原因1: 开发模式下浏览器禁用了缓存。
解决: F12 → Network → 把 "Disable cache" 的勾去掉。
原因2: Nginx 没重载。
解决:
nginx -t
nginx -s reload
总结
| 步骤 | 操作 |
|---|---|
| 图片缓存 | expires 30d |
| CSS/JS 缓存 | expires 7d |
| HTML 不缓存 | expires -1 |
| 改了文件怎么更新 | 加版本号或改文件名 |
| 验证生效 | F12 看 Response Headers |
提醒: 缓存配置好之后,网站打开速度能快好几倍,特别是图片多的网站。
遇到问题加QQ23979811 协助处理
Nginx设置跨域,前端请求接口跨域配置示例
前言
前端页面和后端接口不在同一个域名下,浏览器报跨域错误。这是前后端分离开发最常见的问题。本文讲一下怎么用 Nginx 配置跨域。
什么是跨域
浏览器的同源策略:协议、域名、端口有一个不一样,就是跨域。
比如:
- 前端:
https://www.example.com - 后端接口:
https://api.example.com
这两个域名不一样,浏览器就不让前端直接请求后端接口。
Nginx 配置跨域
简单配置(允许所有域名)
在接口的 server 块里加:
server {
listen 443 ssl http2;
server_name api.example.com;
# 跨域配置
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
root /var/www/api;
index index.php;
}
指定允许的域名(推荐)
生产环境不要用 *,指定具体域名更安全:
# 允许单个域名
add_header Access-Control-Allow-Origin https://www.example.com;
# 允许多个域名
set $cors_origin "";
if ($http_origin ~* "^https://(www\.example\.com|m\.example\.com)$") {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin;
带 Cookie 的跨域
如果跨域请求要带 Cookie,光加 * 不行:
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Credentials true;
注意: 带 Cookie 的时候,Allow-Origin 不能用 *,必须指定具体域名。
完整配置示例
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.crt;
ssl_certificate_key /etc/nginx/ssl/api.key;
# 跨域配置
set $cors_origin "";
if ($http_origin ~* "^https://(www\.example\.com|admin\.example\.com)$") {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $cors_origin;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header Access-Control-Max-Age 86400;
return 204;
}
root /var/www/api;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
检查配置并重载
nginx -t
nginx -s reload
常见坑
坑1:OPTIONS 请求 404
浏览器发 OPTIONS 预检请求,Nginx 直接返回 404。
原因: 没处理 OPTIONS 请求。
解决: 加上面那个 if ($request_method = 'OPTIONS') 的判断,直接返回 204。
坑2:跨域配置不生效
加了跨域配置还是报跨域错误。
原因1: PHP 后端也输出了跨域头,两边冲突了。
解决: 只留一处跨域配置,要么在 Nginx 配,要么在后端代码里配,不要两边都配。
原因2: Nginx 重载失败了。
解决: 确认 nginx -t 没问题,再 nginx -s reload。
坑3:带 Cookie 的跨域失败
配置了跨域,但是带 Cookie 请求还是失败。
原因: Allow-Origin 用了 *,浏览器不允许。
解决: 改成具体域名,加上 Access-Control-Allow-Credentials true。
坑4:POST 请求跨域失败
GET 请求没问题,POST 请求跨域了。
原因: 没允许 POST 方法或者 Content-Type 头。
解决: 把 POST 和 Content-Type 加到 Allow-Methods 和 Allow-Headers 里。
总结
| 配置项 | 作用 |
|---|---|
Access-Control-Allow-Origin |
允许哪个域名访问 |
Access-Control-Allow-Methods |
允许哪些请求方法 |
Access-Control-Allow-Headers |
允许哪些请求头 |
Access-Control-Allow-Credentials |
是否允许带 Cookie |
Access-Control-Max-Age |
预检请求缓存时间 |
生产环境建议: 不要用 *,指定具体域名更安全。
遇到问题加QQ23979811 协助处理
MySQL 事务隔离级别
MySQL 事务隔离级别
事务是数据库的重要特性,隔离级别决定了事务之间互相影响的程度。这篇讲清楚四个隔离级别。
1. 事务的 ACID 特性
- 原子性(Atomicity): 事务要么全部成功,要么全部失败
- 一致性(Consistency): 事务前后数据库状态一致
- 隔离性(Isolation): 多个事务之间互不干扰
- 持久性(Durability): 事务提交后永久保存
2. 三个并发问题
脏读: 一个事务读到另一个事务还没提交的数据
不可重复读: 同一个事务内,两次读同一行数据结果不一样(被别的事务修改了)
幻读: 同一个事务内,两次查询返回的行数不一样(被别的事务插入/删除了)
3. 四个隔离级别
1)读未提交(Read Uncommitted)
- 最低级别,能读到未提交的数据
- 会出现脏读、不可重复读、幻读
2)读提交(Read Committed)
- 只能读到已提交的数据
- 解决脏读,但还有不可重复读、幻读
- Oracle 默认级别
3)可重复读(Repeatable Read)
- 同一个事务内多次读同一行结果一致
- 解决脏读、不可重复读,但还有幻读
- MySQL InnoDB 默认级别
4)串行化(Serializable)
- 最高级别,事务串行执行
- 解决所有问题,但性能最差
4. 查看和设置
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别(会话级别)
SET SESSION transaction_isolation = 'READ-COMMITTED';
-- 设置全局隔离级别
SET GLOBAL transaction_isolation = 'READ-COMMITTED';
5. InnoDB 的优化
MySQL InnoDB 在可重复读级别下,通过 MVCC(多版本并发控制)和间隙锁,也解决了幻读问题,比标准 SQL 定义的更强。
6. 实际开发怎么选
- 大多数场景用默认的可重复读就够了
- 对一致性要求极高的场景(金融),用串行化
- 报表分析等读多写少的场景,可以调低到读提交提高性能
总结
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| 读未提交 | 有 | 有 | 有 | 最好 |
| 读提交 | 无 | 有 | 有 | 好 |
| 可重复读 | 无 | 无 | 无(InnoDB) | 一般 |
| 串行化 | 无 | 无 | 无 | 最差 |
MySQL 默认可重复读,日常开发不用改,理解原理就行。
Nginx 负载均衡配置
Nginx 负载均衡配置
网站流量大了,单台服务器扛不住,就需要负载均衡。这篇讲 Nginx 怎么配负载均衡。
1. 什么是负载均衡
简单说就是把请求分发到多台服务器上,分担压力,提高网站可用性。
2. 基础配置
http {
# 定义后端服务器组
upstream backend {
server 192.168.1.101:80;
server 192.168.1.102:80;
server 192.168.1.103:80;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
3. 负载均衡策略
轮询(默认):
按顺序逐个分发,每台服务器轮流来。
加权轮询:
服务器性能不一样,给好的服务器多分点流量。
upstream backend {
server 192.168.1.101 weight=3;
server 192.168.1.102 weight=2;
server 192.168.1.103 weight=1;
}
ip_hash(会话保持):
同一个 IP 的请求永远分发到同一台服务器,保持登录状态。
upstream backend {
ip_hash;
server 192.168.1.101;
server 192.168.1.102;
}
4. 健康检查
服务器挂了自动跳过,不把请求发过去。
upstream backend {
server 192.168.1.101 max_fails=3 fail_timeout=30s;
server 192.168.1.102 max_fails=3 fail_timeout=30s;
}
- max_fails:失败几次标记为下线
- fail_timeout:多久后重新检查
5. 常用配置说明
location / {
proxy_pass http://backend;
# 传递真实IP
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_connect_timeout 5s;
proxy_read_timeout 60s;
}
6. 常见问题
1)Session 丢失:
- 多台服务器 Session 不共享
- 解决方案:Session 存 Redis,或者用 ip_hash
2)静态文件 404:
- 每台服务器都要有静态文件
- 或者用共享存储
3)服务器性能不均:
- 用 weighted 加权轮询
总结
Nginx 负载均衡要点:
- upstream 定义后端服务器组
- 轮询、加权、ip_hash 三种策略
- 配置健康检查自动下线故障服务器
- Session 要共享或用 ip_hash
做网站架构,负载均衡是必备技能。
PHP 字符串处理函数
PHP 字符串处理函数
字符串处理是 PHP 开发最常用的操作,整理一份常用函数清单。
1. 字符串长度
// 英文字符串长度
echo strlen("hello world"); // 11
// 中文字符串长度(mbstring扩展)
echo mb_strlen("你好世界", "UTF-8"); // 4
2. 截取字符串
// 英文截取
echo substr("hello world", 0, 5); // hello
// 中文截取
echo mb_substr("你好世界", 0, 2, "UTF-8"); // 你好
3. 查找替换
// 查找位置
echo strpos("hello world", "world"); // 6
// 替换
echo str_replace("world", "PHP", "hello world"); // hello PHP
// 大小写转换
echo strtoupper("hello"); // HELLO
echo strtolower("HELLO"); // hello
4. 去除空格
// 去除两端空格
echo trim(" hello "); // hello
// 去除左侧空格
echo ltrim(" hello"); // hello
// 去除右侧空格
echo rtrim("hello "); // hello
5. 字符串分割
// 分割成数组
$arr = explode(",", "a,b,c");
print_r($arr); // Array([0]=>a [1]=>b [2]=>c)
// 数组合并成字符串
echo implode(",", $arr); // a,b,c
6. 格式化输出
// 格式化字符串
echo sprintf("你好,%s!今年%d岁", "张三", 25);
// 你好,张三!今年25岁
7. HTML 转义
// 转义特殊字符
echo htmlspecialchars("<script>alert(1)</script>");
// <script>alert(1)</script>
8. 常用场景
截取标题:
function subTitle($str, $len = 20) {
if (mb_strlen($str, "UTF-8") > $len) {
return mb_substr($str, 0, $len, "UTF-8") . "...";
}
return $str;
}
过滤空格和特殊字符:
function filterStr($str) {
$str = trim($str);
$str = str_replace(" ", "", $str);
return $str;
}
总结
字符串处理常用函数:
- 长度:strlen / mb_strlen
- 截取:substr / mb_substr
- 查找替换:strpos / str_replace
- 分割合并:explode / implode
- 去除空格:trim
做 PHP 开发这些函数一定要用熟。