入门
基本配置
Nginx 配置使用分层块结构。worker_processes 设置进程数。http 块包含 server 块,server 块包含 location 块。重载前务必用 nginx -t 测试。
# /etc/nginx/nginx.conf
worker_processes auto;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name example.com;
location / {
root /var/www/html;
index index.html;
}
}
}
# Test config: nginx -t
# Reload: nginx -s reload
# Start: systemctl start nginx指令与上下文
上下文嵌套:main > events/http/mail > server > location。简单指令以分号结尾;块指令使用大括号。某些指令只在特定上下文中有效。
# Contexts are hierarchical blocks
# Global context (main) - outside any block
user nginx;
worker_processes auto;
# events context
events {
worker_connections 1024;
}
# http context
http {
# server context
server {
listen 80;
# location context
location / {
root /var/www/html;
}
}
}
# Directives: simple (name value;) or block (name { ... })测试与重载
nginx -t 检查语法并尝试打开引用的文件(证书、日志)。重载前务必运行。reload 发送 SIGHUP——worker 完成当前请求后重载。restart 会导致短暂停机。
# test configuration for syntax errors
nginx -t
# test and print full config
nginx -T
# reload config without dropping connections
nginx -s reload
# stop gracefully (finishes current requests)
nginx -s quit
# stop immediately
nginx -s stop
# reopen log files (after log rotation)
nginx -s reopen
# systemd alternative
systemctl reload nginx
systemctl restart nginxMain 与 HTTP 上下文
worker_processes 和 events 位于 main 上下文。路由、代理、缓存和 server 块位于 http。SSL、日志和 gzip 可在 http 中设置并被 server/location 继承。
# main context - global settings
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
}
http {
# all HTTP-related config lives here
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}包含文件
include 保持配置模块化。conf.d 通配符(*.conf)是现代约定。sites-enabled 配合符号链接是 Debian/Ubuntu 的模式。mime.types 将文件扩展名映射到 Content-Type。
# main config: /etc/nginx/nginx.conf
http {
include /etc/nginx/mime.types;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
# /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name example.com;
root /var/www/html;
}
# Disable a site: remove the file from sites-enabled/
# Available sites kept in sites-available/, symlinked into enabled/命令行操作
nginx -V 显示编译时选项,包括内置了哪些模块。-g 设置覆盖配置的指令。-c 用于在不改动默认配置的情况下测试备用配置。
# start nginx (if not managed by systemd)
nginx
# specify a custom config file
nginx -c /path/to/nginx.conf
# set a global directive from command line
nginx -g "worker_processes 4;"
# test config
nginx -t
# print version and build configuration
nginx -V
# send signal to master process
nginx -s reload
nginx -s stop服务器块
基本服务器块
server 块定义一个虚拟主机。listen 设置端口。server_name 匹配 Host 头。root 设置文档根目录。index 指定目录请求时提供哪些文件。
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}多个服务器名
Nginx 按精确匹配、通配符(*.foo.com)、正则的顺序匹配 server_name。第一个匹配的 server 块处理请求。在同一端口上为不同应用使用不同的块。
server {
listen 80;
server_name example.com www.example.com;
# ...
}
server {
listen 80;
server_name api.example.com;
# ...
}
# Wildcard: matches any single-level subdomain
server_name *.example.com;
# Regex (starts with ~)
server_name ~^www\d+\.example\.com$;默认服务器
default_server 处理 Host 头不匹配任何 server_name 的请求。下划线是一种约定(无效主机名,永不匹配真实流量)。return 444 不返回响应直接关闭连接。
# mark a server block as default for the port
server {
listen 80 default_server;
server_name _;
return 444; # drop connection (no response)
}
# handles all requests that don't match another server_name
# _ is an invalid hostname, used as catch-all convention
# useful to block requests with no matching Host headerlisten 指令
listen 可以绑定到特定地址、IPv6、UNIX socket,或设置 default_server。ssl 参数为该监听器启用 TLS。一个 server 块中允许多个 listen 指令。
# listen on port 80
listen 80;
# IPv6
listen [::]:80;
# HTTPS (ssl flag)
listen 443 ssl;
listen [::]:443 ssl;
# specific address
listen 127.0.0.1:8080;
# default server for this port
listen 80 default_server;
# listen on UNIX socket
listen unix:/var/run/nginx.sock;服务器名匹配
匹配优先级为精确 > 前导通配符 > 后置通配符 > 正则。正则按配置顺序检查,因此第一个匹配生效。如果 server 名很长或很多,请增大哈希桶大小。
# Order of precedence (highest to lowest):
# 1. Exact name: example.com
# 2. Longest wildcard starting with *: *.example.com
# 3. Longest wildcard ending with *: www.*
# 4. First matching regex (in config order): ~^www\d+\.
# If nothing matches, the default_server handles it
# Tune hash sizes for many server names
http {
server_names_hash_bucket_size 64;
server_names_hash_max_size 512;
}全捕获服务器
全捕获服务器防止带有任意 Host 头的请求到达你的应用。return 301 重定向到已知主机。return 444 直接断 开连接,适用于抵御直接访问你 IP 的扫描器。
# catch all requests regardless of host header
server {
listen 80 default_server;
server_name _; # invalid name, never matches real host
return 301 https://$host$request_uri;
}
# or drop the connection silently
server {
listen 80 default_server;
server_name _;
return 444;
}
# redirect unknown subdomains to main site
server {
listen 80 default_server;
return 301 https://example.com;
}Location 块
Location 修饰符
修饰符改变匹配行为:=(精确)、^~(前缀,跳过正则)、~(区分大小写正则)、~*(不区分大小写正则)、无(前缀)。正则适合文件扩展名;前缀适合路径树。
# exact match - fastest, stops search if matched
location = /favicon.ico {
log_not_found off;
}
# prefix match with ^~ - skips regex if longest prefix matches
location ^~ /static/ {
root /var/www;
}
# case-sensitive regex
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
}
# case-insensitive regex
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 30d;
}
# prefix match (no modifier)
location / {
try_files $uri $uri/ /index.php;
}Location 优先级
Nginx 先评估精确(=),然后前缀匹配(最长优先),然后按配置顺序的正则。^~ 使前缀匹配跳过正则评估。理解此顺序可避免重叠 location 的意外。
# Priority order (highest to lowest):
# 1. = exact match
# 2. ^~ prefix match (skips regex if matched)
# 3. ~ ~* regex (first match in config order)
# 4. (none) prefix match (longest wins)
location = /api {
# exact, highest priority
}
location ^~ /assets/ {
# prefix, beats regex
}
location ~ \.php$ {
# regex
}
location /api/ {
# prefix, lowest of these
}root 与 alias
root 将 URI 追加到其值后;alias 替换匹配的前缀。当 location 映射到目录树的子目录时用 root。当 URI 路径与文件系统路径不同时用 alias。alias 配合正则需要 $1 捕获。
# root: appends the full URI to the path
# request /img/cat.png -> /var/www/img/cat.png
location /img/ {
root /var/www;
}
# alias: replaces the matched location part
# request /img/cat.png -> /data/images/cat.png
location /img/ {
alias /data/images/;
}
# alias with regex requires capturing the path
location ~ ^/img/(.*)$ {
alias /data/images/$1;
}try_files 指令
try_files 按顺序检查每个参数;最后一个是回退(不检查文件)。$uri 检查文件,$uri/ 检查目录。回退可以是 URI、=code 或 @named。对 SPA 和 CMS 路由至关重要。
# try files in order, fall back to last option
location / {
try_files $uri $uri/ /index.html;
}
# serve index.php if file/dir not found (pretty URLs)
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# return 404 if nothing matches
location / {
try_files $uri =404;
}
# named location as fallback
location / {
try_files $uri $uri/ @drupal;
}命名 Location
命名 location(@name)是内部专用的——永远不会被请求 URI 直接匹配。它们是 try_files、error_page 和 rewrite 的目标。适合在多个 location 间共享配置块(如回退处理器)。
# named locations start with @, can't be reached externally
# used as try_files fallback or rewrite target
location / {
try_files $uri $uri/ @app;
}
location @app {
fastcgi_pass unix:/run/php.sock;
# ... fastcgi config
}
# useful for organizing common error logic
location @error_page {
return 500 "Internal Server Error";
}
# can also be used with error_page
error_page 404 @fallback;
location @fallback {
proxy_pass http://backend;
}嵌套 Location
location 可以嵌套。内部 location 继承并可以覆盖外部 location 的指令。适用于在更宽泛的前缀内特殊处理某个路径(例如上传端点设置更大的 body 大小,或从某个子路径提供静态文件)。
location /api {
# /api/users, /api/posts handled here
proxy_pass http://backend;
location /api/upload {
# override for upload endpoint
client_max_body_size 50M;
proxy_pass http://upload_backend;
}
location ~ ^/api/static/(.*)$ {
# regex inside prefix location
root /var/www;
}
}反向代理
基本 proxy_pass
proxy_pass 将请求转发到上游服务器。proxy_pass 末尾的斜杠会导致匹配的 location 前缀被替换。没有末尾斜杠时,原始 URI 原样传递。
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://localhost:3000;
}
}
# proxy_pass with URI replacement (trailing slash matters)
location /api/ {
proxy_pass http://backend/; # /api/foo -> /foo
}
# without trailing slash, URI is preserved
location /api/ {
proxy_pass http://backend; # /api/foo -> /api/foo
}代理头
默认 nginx 不传递客户端 IP 或原始主机。设置 X-Real-IP 和 X-Forwarded-For 让后端识别真实客户端。X-Forwarded-Proto 告知原始协议,对 HTTPS 重定向逻辑至关重要。
location / {
proxy_pass http://backend;
# preserve original host
proxy_set_header Host $host;
# real client IP (not nginx's)
proxy_set_header X-Real-IP $remote_addr;
# forwarded-for chain
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# scheme (http or https)
proxy_set_header X-Forwarded-Proto $scheme;
# original host
proxy_set_header X-Forwarded-Host $host;
}Upstream 代理
upstream 块定义一个命名的服务器池,供 proxy_pass 引用。这实现了负载均衡、健康检查和 keepalive 连接。upstream 变量可用于记录哪个后端处理了每个请求。
# define a named pool of backend servers
upstream backend {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
# useful upstream variables:
# $upstream_addr, $upstream_status,
# $upstream_response_time, $upstream_cache_status代理超时
proxy_connect_timeout 是 TCP 握手限制;读/写超时适用于数据传输期间的空闲期。为慢端点(导出、AI 推理)增大 read_timeout。upstream keepalive 复用连接以降低延迟。
location / {
proxy_pass http://backend;
# connect timeout (TCP handshake to upstream)
proxy_connect_timeout 5s;
# time between reads from upstream (response)
proxy_read_timeout 60s;
# time between writes to upstream (request body)
proxy_send_timeout 60s;
# for slow backends (large uploads/downloads)
proxy_read_timeout 300s;
}
# keepalive connections to upstream
upstream backend {
server 10.0.0.1:8080;
keepalive 32;
}代理缓冲
缓冲将上游响应存储在内存/磁盘后再发送给客户端——对慢客户端高效但会破坏流式传输。对 SSE、WebSocket 或分块流式传输请关闭。buffer_size 是第一部分(头);buffers 持有其余部分。
location / {
proxy_pass http://backend;
# enable buffering (default on)
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# busy buffers during streaming
proxy_busy_buffers_size 16k;
# disable for streaming/SSE
proxy_buffering off;
proxy_cache off;
}
# Server-Sent Events or streaming responses
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
}WebSocket 升级
WebSocket 需要 HTTP/1.1 加上 Upgrade 和 Connection 头。map 块将 Upgrade 头转换为正确的 Connection 值(upgrade 对 close)。较长的 proxy_read_timeout 防止空闲 WebSocket 被关闭。
# Map the Upgrade header to a Connection value
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# keepalive for long-lived connections
proxy_read_timeout 3600s;
}
}负载均衡
Upstream 块
upstream 块定义后端服务器。默认负载均衡是轮询。max_fails 和 fail_timeout 配置被动健康检查:在 fail_timeout 内失败 max_fails 次后,服务器被标记为下线 fail_timeout 秒。
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com;
server backend3.example.com max_fails=3 fail_timeout=30s;
}
server {
location / {
proxy_pass http://backend;
}
}
# default algorithm: round-robin
# max_fails: failed attempts before marking server down
# fail_timeout: window for counting fails AND downtime duration负载均衡方法
round-robin 均匀分配;least_conn 发送给负载最低的服务器;ip_hash 让客户端保持在同一服务器(通过 IP 实现粘性会话);hash 按自定义键分配。consistent 参数在增删服务器时最小化重分布。
# round-robin (default) - even distribution
upstream backend {
server s1.example.com;
server s2.example.com;
}
# least connections - fewest active connections
upstream backend {
least_conn;
server s1.example.com;
server s2.example.com;
}
# ip_hash - sticky by client IP (session persistence)
upstream backend {
ip_hash;
server s1.example.com;
server s2.example.com;
}
# generic hash (key-based, e.g., by URI or header)
upstream backend {
hash $request_uri consistent;
server s1.example.com;
server s2.example.com;
}加权轮询
weight 按比例控制流量分配。默认权重为 1。为更强大的服务器设置更高权重,使其承担更大份额。weight 适用于 round-robin、least_conn 和 ip_hash。
upstream backend {
server s1.example.com weight=3; # 3x traffic
server s2.example.com weight=2; # 2x traffic
server s3.example.com weight=1; # 1x traffic (default)
}
# total weight = 6
# s1 gets 3/6 = 50%, s2 gets 2/6 = 33%, s3 gets 1/6 = 17%
# useful when servers have different capacity
# e.g., a 4-core server gets weight=4, a 2-core gets weight=2健康检查
开源 nginx 使用基于真实请求失败的被动健康检查(max_fails/fail_timeout)。NGINX Plus 增加了定期轮询 URI 的主动健康检查。服务器必须连续通过 'passes' 次检查才能重新标记为健康。
upstream backend {
server s1.example.com max_fails=3 fail_timeout=30s;
server s2.example.com max_fails=3 fail_timeout=30s;
# passive health check (default behavior):
# 3 failures within 30s marks the server down
# it stays down for fail_timeout (30s) before retry
}
# NGINX Plus active health check
upstream backend {
server s1.example.com;
server s2.example.com;
health_check interval=10s fails=3 passes=2;
health_check uri=/health;
}会话保持
ip_hash 通过客户端 IP 提供粘性会话,但如果 IP 变化(移动网络)则会失效。hash 指令配合 consistent 在 池变化时提供最小的键重分布。sticky cookie(Plus 版)设置 cookie 将客户端固定到某台服务器。
# ip_hash: routes by client IP (uses first 3 IPv4 octets)
upstream backend {
ip_hash;
server s1.example.com:8080;
server s2.example.com:8080;
}
# sticky cookie (NGINX Plus only)
upstream backend {
sticky cookie srv_id expires=1h domain=.example.com path=/;
server s1.example.com;
server s2.example.com;
}
# hash with consistent parameter for minimal redistribution
upstream backend {
hash $http_x_session_id consistent;
server s1.example.com;
server s2.example.com;
}备用服务器
标记了 backup 参数的服务器仅在所有主服务器全部故障时才接收流量。常见模式:主服务器提供应用;备用服务器提供静态维护页面。备用服务器不参与正常负载均衡。
upstream backend {
server s1.example.com;
server s2.example.com;
# used only when all primaries are down
server s3.example.com backup;
server s4.example.com backup;
}
# backup servers receive traffic ONLY when all
# non-backup servers are unavailable
# useful for a maintenance page or DR fallback
upstream backend {
server primary.example.com;
server standby.example.com backup;
}SSL/TLS 配置
基本 HTTPS 服务器
listen 上的 ssl 参数启用 TLS。ssl_certificate 指向证书链(服务器证书 + 中间证书);ssl_certificate_key 指向私钥。始终使用 fullchain.pem 以便客户端能验证证书链。
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}SSL 证书设置
Let's Encrypt 将证书存储在 /etc/letsencrypt/live/<域名>/ 下。fullchain.pem 包含中间证书;privkey.pem 是私钥。nginx 支持每个服务器多种证书类型,根据客户端能力选择。
# Let's Encrypt paths
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# multiple cert types (RSA + ECDSA) on one server
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example.com.rsa.crt;
ssl_certificate_key /etc/ssl/example.com.rsa.key;
ssl_certificate /etc/ssl/example.com.ecdsa.crt;
ssl_certificate_key /etc/ssl/example.com.ecdsa.key;
}HTTP 到 HTTPS 重定向
最干净的模式是使用专用的 80 端口服务器进行重定向。$host 保留原 始主机名。在合并块中使用 if ($scheme) 可以工作,但独立服务器方式更受推荐,可避免 'if is evil' 陷阱。
# redirect all HTTP traffic to HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
# single server block handling both
server {
listen 80;
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example.com.crt;
ssl_certificate_key /etc/ssl/example.com.key;
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}SSL 协议与密码套件
禁用旧协议(SSLv3、TLSv1.0/1.1)。TLS 1.3 最快最安全。使用 Mozilla SSL 配置生成器获取经过测试的配置。shared:SSL 缓存可加速跨 worker 的会话恢复。
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example.com.crt;
ssl_certificate_key /etc/ssl/example.com.key;
# modern configuration (Mozilla SSL Config Generator)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# session settings
ssl_session_tickets off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
}HSTS
HSTS 告知浏览器始终对该域名使用 HTTPS,防止 SSL 剥离攻击。max-age 单位为秒(31536000 = 1 年)。includeSubDomains 覆盖所有子域名。preload 将站点提交到浏览器内置列表——基本不可逆,谨慎使用。
server {
listen 443 ssl;
server_name example.com;
# HSTS: force HTTPS for 1 year (include subdomains)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# preload list (be very careful — requires permanent HTTPS)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
# Preload commits your domain to a browser-baked HTTPS-only list.
# Only enable preload after verifying all subdomains work on HTTPS.OCSP 装订
OCSP 装订让 nginx 获取并提供证书的吊销状态,省去客户端访问 OCSP 响应器的一趟往返。ssl_trusted_certificate 用于验证 OCSP 响应。需要 resolver 让 nginx 能访问 OCSP 服务器。
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/ssl/example.com/privkey.pem;
ssl_stapling on;
ssl_stapling_verify on;
# trust chain for verification
ssl_trusted_certificate /etc/ssl/example.com/chain.pem;
# DNS resolver needed for OCSP requests
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
}静态文件服务
提供静态文件
root 定义文档根目录;nginx 将请求 URI 映射到文件路径。try_files 检查文件、然后目录,然后返回 404。在 server 级别设置 root 让所有 location 继承,但可以按 location 覆盖。
server {
listen 80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
# root set at server level is inherited by all locations
# request /images/logo.png -> /var/www/html/images/logo.png
# request /css/style.css -> /var/www/html/css/style.cssindex 指令
index 列出当请求针对目录时要尝试的文件。第一个存在的文件将被提供。如果都不存在且 autoindex 关闭,nginx 返回 403。顺序很重要——将首选文件列在前面。
server {
listen 80;
root /var/www/html;
# try these files in order when request targets a directory
index index.html index.htm index.php;
location / {
try_files $uri $uri/ =404;
}
}
# request / -> tries /var/www/html/index.html, then index.htm
# request /about/ -> tries /var/www/html/about/index.html
# if no index file found and autoindex off -> 403 ForbiddenSPA 的 try_files
SPA 使用客户端路由,因此未知路径必须提供 index.html。try_files $uri $uri/ /index.html 正好做到这一点。用 immutable 缓存哈希资源一年,因为其内容寻址文件名保证内容变化时 URL 也变化。
# Single Page Application (React, Vue, Angular)
server {
listen 80;
root /var/www/spa;
location / {
# serve static files, fall back to index.html for client routing
try_files $uri $uri/ /index.html;
}
# cache static assets aggressively (filename usually hashed)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}自动索引(目录列表)
autoindex 生成 HTML 目录列表。适用于文件下载站点。关闭 exact_size 提高可读性,开启 localtime 显示正确时间戳。切勿在有敏感文件的目录上启用——务必配合访问控制。
location /files/ {
autoindex on;
autoindex_exact_size off; # human-readable sizes (KB, MB)
autoindex_localtime on; # show local time instead of UTC
root /var/www;
}
# /files/ -> directory listing
# /files/doc.pdf -> downloads the file
# /files/sub/ -> listing of subdirectory
# disable for security on sensitive directories
location /private/ {
autoindex off;
deny all;
}过期头
expires 设置 Cache-Control 的 max-age。immutable 告知浏览器文件永不变(跳过重新验证)。对于哈希资源(file.abc123.js),1 年 + immutable 是理想选择。HTML 应始终重新验证以便用户立即获取更新。
# cache static assets for a year
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# never cache HTML (always revalidate)
location ~* \.html$ {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# short cache for API responses
location /api/ {
add_header Cache-Control "no-cache";
}禁用静态文件日志
静态资源和 favicon/robots 请求会使访问日志变得杂乱。access_log off 停止记录;log_not_found off 抑制 404 日志条目。这使日志聚焦于有意义的流量,并减少繁忙站点的磁盘 I/O。
# don't log requests for static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
access_log off;
log_not_found off;
expires 1y;
}
# don't log favicon requests (often 404 from browsers)
location = /favicon.ico {
log_not_found off;
access_log off;
}
# don't log robots.txt
location = /robots.txt {
log_not_found off;
access_log off;
}Gzip 压缩
启用 Gzip
gzip 压缩响应,减少带宽。gzip_vary 为缓存添加 'Vary: Accept-Encoding'。gzip_proxied any 为代理请求压缩。comp_level 6 平衡 CPU 和大小。min_length 256 跳过微小响应(开销超过收益)。
http {
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/xml
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
}Gzip 类型
gzip_types 列出要压缩的 MIME 类型。text/html 默认总是被压缩。不要压缩已经压缩过的二进制格式(图片、视频、归档)——浪费 CPU 却没有体积减少,甚至可能增大输出。
http {
gzip on;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
application/rss+xml
application/atom+xml
image/svg+xml
font/ttf
font/otf
application/vnd.ms-fontobject;
# text/html is always compressed (implicitly, cannot disable)
# DON'T compress already-compressed: jpg, png, gif, mp4, zip, gz
}Gzip 压缩级别
comp_level 在 CPU 与压缩之间权衡。6 是最佳点——9 使用多得多的 CPU 却只换来不到 2% 的体积减少。min_length 避免压缩 gzip 头开销超 过节省量的微小响应。gzip_disable 排除 gzip 支持损坏的旧版 IE6。
http {
gzip on;
gzip_comp_level 6; # 1-9, higher = more compression, more CPU
# level 1: fast, low compression
# level 6: good balance (recommended default)
# level 9: slow, marginal size gain
# minimum response size to compress (bytes)
gzip_min_length 256;
# disable gzip for old browsers
gzip_disable "MSIE [1-6]\.";
}Gzip 静态
gzip_static 提供预构建的 .gz 文件,消除运行时压缩的 CPU 开销。构建步骤在原始文件旁创建 .gz 文件。nginx 向发送 Accept-Encoding: gzip 的客户端提供 .gz,向其他客户端提供原始文件。非常适合静态资源管线。
# serve pre-compressed .gz files (no runtime compression cost)
# requires ngx_http_gzip_static_module
location / {
gzip_static on;
# on: serve .gz if it exists
# always: serve .gz even without Accept-Encoding header
root /var/www/html;
}
# pre-compress files at build time:
# find /var/www/html -type f ( -name "*.css" -o -name "*.js" \
# -o -name "*.html" ) -exec gzip -k -9 {} \;
# creates file.css.gz, file.js.gz alongside the originalsBrotli
Brotli 在文本内容上优于 gzip,尤其在较高压缩级别。它需要第三方 ngx_brotli 模块。brotli_static 提供预构建的 .br 文件。现代浏览器在 Accept-Encoding 中声明 'br',因此 nginx 选择最佳可用编码。
# requires ngx_brotli module (not built-in)
http {
brotli on;
brotli_comp_level 6;
brotli_types
text/plain
text/css
application/javascript
application/json
image/svg+xml;
# serve pre-compressed .br files
brotli_static on;
}
# Brotli typically 10-20% smaller than gzip for text
# supported by all modern browsers (not IE)
# nginx can serve both .br and .gz; picks based on Accept-EncodingGzip 代理
默认 nginx 不压缩代理响应(避免对已压缩的上游双重压缩)。gzip_proxied any 为所有代理请求启用压缩。gzip_vary 确保缓存为压缩/未压缩客户端存储不同版本。
http {
gzip on;
gzip_proxied any;
# gzip_proxied controls compression for proxied requests:
# off: never compress proxied requests
# expired: compress if response has Expires header
# no-cache: compress if Cache-Control: no-cache
# no-store: compress if Cache-Control: no-store
# private: compress if Cache-Control: private
# no_last_modified: compress if no Last-Modified header
# no_etag: compress if no ETag header
# auth: compress if Authorization header present
# any: compress all proxied requests
gzip_vary on; # add "Vary: Accept-Encoding" header
}缓存
代理缓存
proxy_cache 将上游响应存储在磁盘上。keys_zone 定义缓存键的共享内存区(10m 约等于 80,000 个键)。max_size 限制磁盘使用;inactive 移除在该时间内未访问的条目。use_temp_path=off 直接写入缓存以提高速度。
http {
# define cache zone on disk
proxy_cache_path /var/cache/nginx levels=1:2
keys_zone=api_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
location /api/ {
proxy_pass http://backend;
proxy_cache api_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
}
}
}缓存区
levels=1:2 从缓存键的 MD5 哈希创建两级目录树,避免一个目录中文件过多。按内容类型分设独立缓存区可分别调优 max_size 和 inactive——静态资源缓存更久,API 缓存更短。
http {
proxy_cache_path /var/cache/nginx
levels=1:2 # directory hierarchy depth
keys_zone=my_cache:10m # zone name + shared memory size
max_size=1g # max disk size for cache
inactive=60m # remove if not accessed in 60min
use_temp_path=off; # write directly (faster, no temp copy)
# multiple zones for different content types
proxy_cache_path /var/cache/static levels=1:2
keys_zone=static:10m max_size=500m inactive=24h;
proxy_cache_path /var/cache/api levels=1:2
keys_zone=api:10m max_size=200m inactive=10m;
}缓存键
缓存键决定缓存唯一性。默认键可能在不同虚拟主机间产生冲突——请包含 $host。对于用户特定内容,请包含用户标识。proxy_cache_bypass 跳过缓存读取(但仍会存储);proxy_no_cache 不存储响应。
location / {
proxy_pass http://backend;
proxy_cache api_cache;
# default key: $scheme$proxy_host$request_uri
proxy_cache_key "$scheme$request_method$host$request_uri";
# include user-specific data to avoid cross-user leaks
proxy_cache_key "$scheme$host$request_uri$http_x_user_id";
# bypass cache for authenticated users
proxy_cache_bypass $http_authorization;
proxy_no_cache $http_authorization;
}缓存有效期
proxy_cache_valid 按状态码设置 TTL。0s 或省略某状态码可阻止缓存。proxy_cache_lock 串行化同一键的并发缓存未命中——只有一个请求访问上游;其余等待缓存响应。
location / {
proxy_pass http://backend;
proxy_cache api_cache;
# cache by response status code
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid 500 502 503 504 0s; # don't cache errors
# fallback for any other status
proxy_cache_valid any 1m;
# cache lock: collapse concurrent misses into one upstream call
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
}绕过缓存
proxy_cache_bypass 跳过读取缓存(访问上游)但可能仍会存储响应。proxy_no_cache 跳过存储。X-Cache-Status 头对调试非常宝贵——HIT 表示从缓存提供,MISS 表示从上游获取。
location / {
proxy_pass http://backend;
proxy_cache api_cache;
# bypass cache (always go to upstream)
proxy_cache_bypass $http_authorization $arg_nocache;
# don't store in cache
proxy_no_cache $http_authorization;
# add header showing cache status for debugging
add_header X-Cache-Status $upstream_cache_status;
}
# $upstream_cache_status values:
# MISS, BYPASS, EXPIRED, STALE, UPDATING, REVALIDATED, HIT浏览器缓存
浏览器缓存降低重复访问延迟。immutable 完全跳过重新验证(仅对内容寻址文件名安全)。no-cache 表示每次重新验证(仍允许条件 GET)。no-store 表示永不存储。配合 ETag 可实现高效的 304 响应。
# cache static assets in the browser
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML - revalidate always
location ~* \.html$ {
add_header Cache-Control "no-cache, must-revalidate";
etag on;
}
# API responses - never cache
location /api/ {
add_header Cache-Control "no-store";
}
# public: any cache (browser, CDN) may store
# private: only the browser may store
# immutable: file never changes, skip revalidation
# no-store: never cacheURL 重写
return 指令
return 是最简单的重定向或响应方式,立即停止处理。用 301 做永久重定向,302 做临时重定向。可以返回带 body 的状态码(200、418)或重定向到 URL。重定向时优先用 return 而非 rewrite。
# simplest redirect/rewrite - return stops processing
server {
location /old {
return 301 /new;
}
location /external {
return 301 https://example.com$request_uri;
}
location /api {
return 200 "OK\n";
}
location /teapot {
return 418 "I'm a teapot";
}
}
# return is preferred over rewrite for simple redirectsrewrite 指令
rewrite 用正则匹配 URI 并替换。last 用 新 URI 重新评估 location 匹配(相当于从头开始)。break 停止重写但留在当前 location。redirect/permanent 向浏览器发送重定向。
# rewrite regex replacement [flag]
server {
# rewrite /old/* to /new/* preserving the captured part
rewrite ^/old/(.*)$ /new/$1 permanent;
# rewrite old blog URLs to new structure
rewrite ^/blog/(\d{4})/(\d{2})/(.*)$ /articles/$1/$2/$3 last;
# capture with named groups
rewrite ^/download/(.*)$ /assets/$1 last;
}
# flags:
# last: re-search locations with the new URI
# break: stop rewrite, continue in current location
# redirect: 302 temporary redirect
# permanent: 301 permanent redirectrewrite 标志
last 与 break 是最令人困惑的部分。last 重新运行 location 搜索(当重写指向不同 location 时有用)。break 停止重写但留下(当重写只是调整内部路径时有用)。redirect/permanent 改变浏览器 URL。
location / {
# last: restart location matching with new URI
rewrite ^/api/(.*)$ /backend/$1 last;
# break: stop processing rewrites, stay in current location
rewrite ^/static/(.*)$ /assets/$1 break;
# redirect: 302 temporary redirect (browser URL changes)
rewrite ^/old/(.*)$ /new/$1 redirect;
# permanent: 301 permanent redirect (browser URL changes)
rewrite ^/old/(.*)$ /new/$1 permanent;
}
# avoid rewrite inside if blocks - use location + try_files instead命名捕获
命名捕获 (?<名称>模式) 创建变量($名称),比位置式 $1、$2 更清晰。它们在 location 正则和 rewrite 中都有效。捕获的值在该 location 内可用于 proxy_pass、fastcgi_param、add_header 和 log_format。
# named captures in regex locations
location ~ ^/users/(?<username>[a-z]+)/?$ {
proxy_pass http://backend/users/$username;
}
# named captures in rewrite
location / {
rewrite ^/articles/(?<year>\d{4})/(?<slug>[a-z-]+)/?$
/blog?year=$year&slug=$slug last;
}
# named captures become variables: $year, $slug, $username
# accessible in proxy_pass, fastcgi_pass, headers, etc.
# safer than $1, $2 positional captures for readabilitymap 指令
map 构建从一个变量到另一个变量的查找表,惰性求值。比 if 语句链高效得多且更易读。default 分支处理未匹配的值。正则匹配(以 ~ 为前缀)按顺序检查。
# map creates a new variable based on another variable's value
map $http_host $backend {
default "http://default.backend";
"a.com" "http://a.backend";
"b.com" "http://b.backend";
"~^.*\.app\.com$" "http://app.backend";
}
server {
server_name a.com b.com;
location / {
proxy_pass $backend;
}
}
# map is evaluated lazily and cached per request
# more efficient than if/else chainsif 指令(谨慎使用)
if 在 location 块内有令人意外的行为——可能以不可预测的方式重写配置。唯一普遍安全的用法是 return 和 rewrite。文件存在检查请用 try_files。复杂逻辑请用 map 或独立的 location 块。参见 wiki:'If Is Evil'。
# "if is evil" — use sparingly! Only safe for:
# return / rewrite, and with these checks:
# -f, -d, -e, -x (file/dir/exists/executable)
# =, !=, ~, ~* (string/regex comparison)
location / {
if ($request_method = POST) {
return 405;
}
if ($http_user_agent ~* "bot") {
return 403;
}
# BAD: don't do this inside if
# if (-f $request_filename) { ... }
# use try_files instead
try_files $uri $uri/ /index.html;
}重定向
永久重定向(301)
301 表示'永久移动'——浏览器和搜索引擎会缓存它,因此只用于真正永久性的变更。先用 302 测试重定向以避免锁定错误。301 将链接权重(SEO)传递到新 URL。