跳到主要内容

核心指令

本章将详细介绍 Nginx 的核心配置指令,理解这些指令对于正确配置 Nginx 至关重要。

请求处理指令

root 和 alias

这两个指令用于指定文件路径,但行为不同:

# root - 将 URI 追加到路径后面
location /images/ {
root /var/www;
# 请求 /images/photo.jpg -> /var/www/images/photo.jpg
}

# alias - 用指定路径替换匹配部分
location /images/ {
alias /var/www/pictures/;
# 请求 /images/photo.jpg -> /var/www/pictures/photo.jpg
}

重要区别

# root 示例
location /static/ {
root /var/www;
# /static/file.css -> /var/www/static/file.css
}

# alias 示例
location /static/ {
alias /var/www/assets/;
# /static/file.css -> /var/www/assets/file.css
}

解释

  • root 保留原始 URI,只添加根路径
  • alias 替换匹配的 location 部分
  • 使用 alias 时,location 通常需要以 / 结尾

index

指定默认首页文件:

# 单个文件
index index.html;

# 多个文件(按顺序查找)
index index.html index.htm index.php;

# 带变量
index index.$geo.html index.html;

try_files

按顺序检查文件是否存在:

# 基本用法
location / {
try_files $uri $uri/ /index.html;
# 1. 检查 $uri 文件
# 2. 检查 $uri/ 目录
# 3. 都不存在,返回 /index.html
}

# 带状态码
location / {
try_files $uri $uri/ =404;
# 都不存在返回 404
}

# 代理到后端
location / {
try_files $uri $uri/ @backend;
}

location @backend {
proxy_pass http://127.0.0.1:8080;
}

解释try_files 是处理 SPA 应用路由的关键指令。

日志指令

access_log

配置访问日志:

# 基本配置
access_log /var/log/nginx/access.log;

# 指定格式
access_log /var/log/nginx/access.log main;

# 关闭日志
access_log off;

# 多个日志
access_log /var/log/nginx/access.log main;
access_log /var/log/nginx/analytics.log analytics;

# 缓冲写入
access_log /var/log/nginx/access.log main buffer=16k flush=5s;

# 条件日志(跳过静态资源)
map $uri $is_static {
~*\.(jpg|jpeg|png|gif|css|js)$ 1;
default 0;
}
access_log /var/log/nginx/access.log main if=$is_static;

error_log

配置错误日志:

# 全局配置
error_log /var/log/nginx/error.log warn;

# 日志级别
error_log /var/log/nginx/error.log debug; # 最详细
error_log /var/log/nginx/error.log info;
error_log /var/log/nginx/error.log notice;
error_log /var/log/nginx/error.log warn; # 推荐
error_log /var/log/nginx/error.log error;
error_log /var/log/nginx/error.log crit;
error_log /var/log/nginx/error.log alert;
error_log /var/log/nginx/error.log emerg; # 最严重

# 关闭错误日志
error_log /dev/null crit;

log_format

自定义日志格式:

# 基本格式
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';

# 详细格式
log_format detailed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'request_time=$request_time '
'upstream_time=$upstream_response_time '
'upstream_addr=$upstream_addr';

# JSON 格式(便于日志分析)
log_format json escape=json
'{"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"http_user_agent":"$http_user_agent"}';

客户端指令

client_max_body_size

限制请求体大小:

# 全局设置
http {
client_max_body_size 8m;
}

# 特定 location
location /upload/ {
client_max_body_size 50m; # 上传接口允许更大文件
}

# 禁用限制(不推荐)
client_max_body_size 0;

解释:超过限制会返回 413 Request Entity Too Large 错误。

client_body_buffer_size

请求体缓冲区大小:

client_body_buffer_size 16k;
# 小于此大小的请求体会存入内存
# 超过则写入临时文件

client_header_buffer_size

请求头缓冲区:

client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
# 单个请求头不超过 1k
# 总请求头不超过 4*8k

超时指令

连接超时

# 客户端保持连接超时
keepalive_timeout 65s;

# 读取客户端请求超时
client_body_timeout 60s;

# 发送响应到客户端超时
send_timeout 60s;

# 读取客户端请求头超时
client_header_timeout 60s;

代理超时

location / {
proxy_pass http://backend;

# 连接后端超时
proxy_connect_timeout 60s;

# 读取后端响应超时
proxy_read_timeout 60s;

# 发送请求到后端超时
proxy_send_timeout 60s;
}

文件处理指令

sendfile

使用系统调用高效传输文件:

# 开启 sendfile(推荐)
sendfile on;

# 配合 tcp_nopush 使用
sendfile on;
tcp_nopush on;
tcp_nodelay on;

解释

  • sendfile 使用零拷贝技术,避免数据在内核和用户空间之间复制
  • tcp_nopush 在 sendfile 模式下,等待数据包填满再发送
  • tcp_nodelay 禁用 Nagle 算法,减少延迟

open_file_cache

文件描述符缓存:

open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

参数说明

  • max:缓存的最大文件描述符数量
  • inactive:非活跃条目的保留时间
  • valid:检查缓存有效性的时间间隔
  • min_uses:文件被访问多少次后才缓存

MIME 类型

types

配置文件扩展名和 MIME 类型的映射:

# 包含默认配置
include mime.types;

# 自定义类型
types {
text/html html htm;
text/css css;
application/javascript js;
application/json json;
image/jpeg jpeg jpg;
image/png png;
image/gif gif;
image/svg+xml svg;
application/pdf pdf;
}

# 默认类型
default_type application/octet-stream;

重写指令

rewrite

URL 重写:

# 基本语法
rewrite regex replacement [flag];

# 示例
rewrite ^/old/(.*)$ /new/$1 permanent; # 301 重定向
rewrite ^/old/(.*)$ /new/$1 redirect; # 302 重定向
rewrite ^/api/(.*)$ /v1/$1 last; # 内部重写
rewrite ^/download/(.*)$ /files/$1 break; # 停止处理

# 正则表达式
rewrite ^/users/(\d+)$ /user.php?id=$1 last;
rewrite ^/posts/(\w+)$ /post.php?slug=$1 last;

标志说明

  • last:停止处理当前 rewrite,重新匹配 location
  • break:停止处理所有 rewrite
  • redirect:返回 302 临时重定向
  • permanent:返回 301 永久重定向

return

直接返回响应:

# 返回状态码
location /forbidden {
return 403;
}

# 返回重定向
location /old {
return 301 https://new.example.com$request_uri;
}

# 返回内容
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}

# 关闭连接(不返回任何内容)
location / {
return 444;
}

if 条件

条件判断(谨慎使用):

# 正确用法
if ($request_method = POST) {
return 405;
}

if ($http_user_agent ~* "bot") {
return 403;
}

if ($http_x_forwarded_proto != "https") {
return 301 https://$host$request_uri;
}

# 检查文件是否存在
if (-f $request_filename) {
# 文件存在
}

if (!-f $request_filename) {
# 文件不存在
}

注意if 指令在某些情况下会有意想不到的行为,尽量使用 try_filesmap 替代。

map 指令

基本用法

# 根据变量值设置新变量
map $http_user_agent $is_mobile {
default 0;
~*mobile 1;
~*android 1;
~*iphone 1;
}

# 使用
server {
location / {
if ($is_mobile) {
return 301 https://m.example.com$request_uri;
}
}
}

常见模式

# 限流白名单
map $remote_addr $limit {
default $binary_remote_addr;
192.168.1.1 ""; # 不限流
10.0.0.0/8 ""; # 内网不限流
}

# 后端选择
map $cookie_backend $backend {
default backend_a;
"new" backend_b;
}

# 文件类型判断
map $uri $is_static {
default 0;
~*\.(jpg|jpeg|png|gif|css|js|woff2)$ 1;
}

split_clients

按比例分流:

split_clients "${remote_addr}" $variant {
10% "canary"; # 10% 流量
* "stable"; # 90% 流量
}

upstream canary {
server 192.168.1.101:8080;
}

upstream stable {
server 192.168.1.102:8080;
}

server {
location / {
proxy_pass http://$variant;
}
}

隐藏和添加头

add_header

添加响应头:

# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

# CORS 头
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";

# 自定义头
add_header X-Request-ID $request_id;
add_header X-Server $hostname;

注意add_header 不会继承到嵌套的 location。

proxy_hide_header

隐藏后端返回的头:

location / {
proxy_pass http://backend;
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}

proxy_set_header

设置发送到后端的请求头:

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;
proxy_set_header X-Forwarded-Proto $scheme;
}

变量速查

请求变量

# 客户端信息
$remote_addr # 客户端 IP
$remote_port # 客户端端口
$remote_user # 认证用户名

# 请求信息
$request_method # 请求方法:GET, POST 等
$request_uri # 完整 URI(含参数)
$uri # URI(不含参数)
$args # 查询参数
$query_string # 同 $args
$request # 完整请求行
$request_length # 请求长度
$request_time # 请求处理时间

# 主机和协议
$host # 请求的主机名
$hostname # 服务器主机名
$scheme # 协议:http 或 https
$server_name # 服务器名称
$server_port # 服务器端口
$server_protocol # 协议版本:HTTP/1.1

# 请求头
$http_user_agent # User-Agent
$http_referer # Referer
$http_host # Host 头
$http_cookie # Cookie
$http_x_forwarded_for # X-Forwarded-For
$http_x_forwarded_proto # X-Forwarded-Proto

响应变量

$status             # 响应状态码
$body_bytes_sent # 发送的字节数
$bytes_sent # 发送的总字节数
$content_length # Content-Length
$content_type # Content-Type
$upstream_addr # 后端地址
$upstream_status # 后端状态码
$upstream_response_time # 后端响应时间

其他变量

$connection         # 连接序列号
$connection_requests # 连接的请求数
$time_local # 本地时间
$time_iso8601 # ISO 8601 时间
$msec # 毫秒时间戳
$request_id # 请求 ID(唯一)
$ssl_protocol # SSL 协议
$ssl_cipher # SSL 加密套件
$cookie_name # Cookie 值

小结

本章学习了:

  1. 路径指令(root、alias、index、try_files)
  2. 日志指令(access_log、error_log、log_format)
  3. 客户端指令(client_max_body_size 等)
  4. 超时指令
  5. 文件处理指令
  6. 重写指令(rewrite、return)
  7. 条件和映射(if、map)
  8. 头部处理指令
  9. 内置变量

练习

  1. 配置 try_files 实现 SPA 路由
  2. 自定义 JSON 格式的日志
  3. 使用 map 实现条件判断
  4. 配置安全响应头

延伸阅读