nginx location 块是控制 Nginx 处理传入请求的最关键指令。它决定哪个配置适用于给定的 URL,使你能够精确地路由流量、提供文件、代理到后端以及应用安全规则。本全面指南涵盖了 location 块语法、正则修饰符、匹配优先级以及 30+ 个生产可用的示例,你可以立即复制和使用。
1. Location 块语法
每个 location 块遵循相同的基本结构:location 关键字、可选的修饰符、用于匹配请求 URI 的模式,以及用花括号包围的指令块。修饰符决定了 Nginx 如何解释模式——作为字面前缀、精确匹配还是正则表达式。
# Location block syntax
# location [modifier] pattern { directives; }
# 1. Exact match -- highest priority
location = /api/health {
return 200 "OK";
add_header Content-Type text/plain;
}
# 2. Prefix priority -- stops regex evaluation
location ^~ /static/ {
root /var/www;
expires 30d;
}
# 3. Case-sensitive regex
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
}
# 4. Case-insensitive regex
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 365d;
add_header Cache-Control "public, immutable";
}
# 5. Standard prefix (no modifier)
location /api/ {
proxy_pass http://backend;
}
# 6. Default catch-all
location / {
try_files $uri $uri/ /index.html;
}2. Location 修饰符详解
Nginx 提供了五种匹配请求 URI 的方式。理解这些修饰符是掌握 Nginx 配置最重要的概念。每个修饰符改变了模式的解释方式,并影响匹配的优先级。
| 修饰符 | 类型 | 说明 | 优先级 |
|---|---|---|---|
| = | Exact | Exact URI match, stops immediately | 1 (highest) |
| ^~ | Prefix priority | Prefix match, skips regex if matched | 2 |
| ~ | Regex (case-sensitive) | PCRE regex, first match in file order wins | 3 |
| ~* | Regex (case-insensitive) | PCRE regex, case-insensitive | 3 |
| (none) | Prefix | Longest prefix match, can be overridden by regex | 4 (lowest) |
=(精确匹配)——完全匹配 URI,不多不少。这是最快的匹配方式,因为 Nginx 找到精确匹配后会立即停止搜索。用于特定路径,如首页或健康检查端点。
# Exact match: only matches /login, not /login/ or /login?next=/
location = /login {
proxy_pass http://auth-service:3000;
}
# Exact match for homepage -- fastest possible match
location = / {
proxy_pass http://homepage-service:3000;
}~(正则,区分大小写)——将模式视为 PCRE 正则表达式,区分大小写。用于在区分大小写的文件系统(Linux)上匹配文件扩展名。
# Case-sensitive regex: matches .php files
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Case-sensitive: matches /API/ but not /api/
location ~ ^/API/ {
proxy_pass http://legacy-api:8080;
}~*(正则,不区分大小写)——将模式视为 PCRE 正则表达式,不区分大小写。由于客户端可能使用任何大小写,Web URL 推荐使用此修饰符。
# Case-insensitive regex: matches .JPG, .jpg, .Jpg, etc.
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|avif)$ {
root /var/www/static;
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
}^~(前缀优先)——前缀匹配,如果匹配成功,则阻止 Nginx 检查任何正则 location 块。当你希望前缀匹配始终优先于正则模式时使用。
# ^~ prefix priority: even if a regex matches /static/..., this wins
location ^~ /static/ {
alias /var/www/app/static/;
expires 30d;
add_header Cache-Control "public";
}
# This regex will NOT override ^~ /static/ above
location ~* \.(css|js)$ {
expires 7d;
}(无修饰符 / 前缀匹配)——默认行为。如果 URI 以给定字符串开头则匹配。但如果正则 location 也匹配该 URI,则正则 location 可以覆盖此匹配。
# Prefix match (no modifier): matches /api/users, /api/orders, etc.
# But can be overridden by a regex location
location /api/ {
proxy_pass http://api-backend:4000;
proxy_set_header Host $host;
}
# This regex WILL override the prefix match above for .json requests
location ~* \.json$ {
add_header Content-Type application/json;
try_files $uri =404;
}3. 匹配优先级顺序
当请求到达时,Nginx 按特定的优先级顺序评估 location 块——而不是按它们在配置文件中出现的顺序。理解此顺序可以避免无数小时的调试意外路由行为。
第一步:精确匹配(=)——Nginx 首先检查所有精确匹配 location。如果有匹配,立即使用并停止所有其他搜索。
第二步:前缀匹配(无修饰符和 ^~)——Nginx 检查所有前缀 location 并记住最长匹配的前缀。如果最长匹配具有 ^~ 修饰符,则立即使用并跳过正则检查。
第三步:正则匹配(~ 和 ~*)——Nginx 按配置文件中出现的顺序检查正则 location。第一个匹配的正则获胜。
第四步:最长前缀回退——如果没有正则匹配,则使用第二步中最长的前缀匹配。
关键洞察:配置文件中正则块的顺序很重要,但前缀块的顺序不重要。Nginx 总是选择最长的前缀匹配,无论顺序如何。
# Priority demonstration -- request: GET /static/logo.png
# Nginx evaluates in this order:
# Step 1: Check exact matches
location = /static/logo.png { } # Match? YES -> Use this, STOP
location = /index.html { } # Not checked (already matched above)
# Step 2: If no exact match, check all prefix locations
location /static/ { } # Prefix matches (8 chars)
location /static/logo { } # Prefix matches (12 chars) -- LONGEST
location ^~ /static/ { } # If this existed, regex would be skipped
# Step 3: Check regex locations (in config file order)
location ~* \.(png|jpg)$ { } # Regex matches -> Use this, STOP
location ~* /static/.* { } # Not checked (first regex already matched)
# Step 4: If no regex matched, use longest prefix from Step 2
# In this case: location /static/logo { }
# ---- Complete example showing priority ----
server {
listen 80;
server_name example.com;
# Priority 1: Exact match (checked first)
location = / {
# Only matches GET /
return 200 "Homepage";
}
# Priority 2: ^~ prefix (blocks regex override)
location ^~ /assets/ {
# All /assets/* requests come here, no regex can override
root /var/www;
expires 30d;
}
# Priority 3: Regex (checked in order, first match wins)
location ~* \.(css|js)$ {
# Matches .css and .js files (except under /assets/)
expires 7d;
}
location ~ /api/v[0-9]+/ {
# Matches /api/v1/, /api/v2/, etc.
proxy_pass http://api-backend;
}
# Priority 4: Longest prefix (fallback if no regex matches)
location /api/ {
proxy_pass http://default-backend;
}
location / {
# Default catch-all (shortest prefix, lowest priority)
try_files $uri $uri/ /index.html;
}
}4. Location 块的正则表达式模式
Nginx location 块中的 PCRE(Perl 兼容正则表达式)模式让你可以匹配复杂的 URL 模式。以下是最常用的模式及其说明。
# ---- File Extension Patterns ----
# Match image files
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|avif|bmp|tiff)$ {
expires 365d;
add_header Cache-Control "public, immutable";
}
# Match web font files
location ~* \.(woff|woff2|ttf|eot|otf)$ {
expires 365d;
add_header Access-Control-Allow-Origin "*";
}
# Match CSS and JavaScript bundles (usually have hash in filename)
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Match source maps (block in production)
location ~* \.map$ {
deny all;
return 404;
}
# ---- Path-Based Patterns ----
# Match versioned API paths: /api/v1/, /api/v2/, /api/v10/
location ~ ^/api/v[0-9]+/ {
proxy_pass http://api-backend;
}
# Match UUID in URL: /users/550e8400-e29b-41d4-a716-446655440000
location ~ ^/users/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ {
proxy_pass http://user-service;
}
# Match numeric IDs: /products/12345
location ~ ^/products/[0-9]+$ {
proxy_pass http://product-service;
}
# Match language prefixes: /en/, /fr/, /zh/, /ja/
location ~ ^/(en|fr|de|es|zh|ja|ko)/ {
try_files $uri $uri/ /index.html;
}
# ---- Negative Patterns (deny specific paths) ----
# Block access to hidden files (.git, .env, .htaccess)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Block access to backup files
location ~* \.(bak|old|orig|save|swp|swo)$ {
deny all;
return 404;
}
# Block access to configuration files
location ~* /(wp-config|config|configuration)\.php$ {
deny all;
}
# ---- Query String Matching (using if inside location) ----
# Note: location blocks match URI path only, not query strings.
# Use "if" for query string matching (use sparingly).
location /search {
# Redirect old search format to new format
if ($arg_q = "") {
return 301 /;
}
proxy_pass http://search-service;
}5. try_files 指令
try_files 指令是 location 块内最常用的指令之一。它告诉 Nginx 按特定顺序检查文件并提供找到的第一个文件,或回退到命名 location 或错误代码。这对于 SPA、带漂亮 URL 的静态站点以及混合静态/动态设置至关重要。
# ---- try_files syntax ----
# try_files file1 file2 ... fallback;
# Tests each file path in order, serves the first one that exists.
# The last argument is special: it can be a URI (internal redirect)
# or an error code (=404, =500).
# ---- SPA Fallback (React, Vue, Angular) ----
location / {
root /var/www/app/dist;
# Try the exact file -> try as directory -> fall back to index.html
try_files $uri $uri/ /index.html;
}
# ---- Static Site with Pretty URLs ----
location / {
root /var/www/blog;
# Try exact file -> try .html extension -> try as directory -> 404
try_files $uri $uri.html $uri/ =404;
}
# ---- Static Files + Dynamic Backend ----
location / {
root /var/www/static;
# Try static file first, then proxy to backend
try_files $uri $uri/ @backend;
}
# Named location for backend proxy
location @backend {
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 X-Forwarded-Proto $scheme;
}
# ---- Maintenance Mode ----
location / {
root /var/www;
# If maintenance.html exists, serve it for all requests
try_files /maintenance.html $uri $uri/ /index.html;
}
# ---- Multi-language SPA ----
location / {
root /var/www/app;
# Try language-specific file, then default, then SPA fallback
try_files $uri $uri/ /$1/index.html /index.html;
}
# ---- try_files with PHP-FPM ----
location / {
root /var/www/html;
index index.php index.html;
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
root /var/www/html;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}6. Location 块中的 Proxy Pass
在 location 块中使用 proxy_pass 是将特定 URL 路径路由到不同后端应用的标准方式。这是微服务路由、API 网关和后端分离的基础。
# ---- Basic Proxy Pass ----
location /api/ {
proxy_pass http://127.0.0.1:4000;
# IMPORTANT: trailing slash behavior
# proxy_pass http://127.0.0.1:4000; -> /api/users -> /api/users
# proxy_pass http://127.0.0.1:4000/; -> /api/users -> /users (strips /api)
}
# ---- Strip Path Prefix ----
# Request: /api/v1/users -> Backend receives: /users
location /api/v1/ {
proxy_pass http://backend-v1/; # Note the trailing slash!
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;
}
# ---- Upstream Block (Load Balancing) ----
upstream api_cluster {
least_conn; # Send to server with fewest connections
server 10.0.0.1:4000 weight=5;
server 10.0.0.2:4000 weight=3;
server 10.0.0.3:4000 backup; # Only used when others are down
keepalive 32; # Keep connections alive to backends
}
location /api/ {
proxy_pass http://api_cluster;
proxy_http_version 1.1;
proxy_set_header Connection ""; # Required for keepalive
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;
# Timeouts
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Retry on failure
proxy_next_upstream error timeout http_502 http_503;
proxy_next_upstream_tries 3;
}
# ---- WebSocket Proxy ----
location /ws/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s; # Keep WebSocket alive for 24h
proxy_send_timeout 86400s;
}
# ---- Microservice Routing ----
location /auth/ {
proxy_pass http://auth-service:3001/;
}
location /users/ {
proxy_pass http://user-service:3002/;
}
location /orders/ {
proxy_pass http://order-service:3003/;
}
location /payments/ {
proxy_pass http://payment-service:3004/;
}7. 静态文件服务
Nginx 擅长提供静态文件。理解 root 和 alias 的区别,以及如何设置正确的缓存头,对于性能优化至关重要。
# ---- root vs alias ----
# ROOT: appends full URI to the root path
# Request: /images/photo.jpg -> File: /var/www/images/photo.jpg
location /images/ {
root /var/www;
# Nginx looks for: /var/www + /images/photo.jpg
}
# ALIAS: replaces the matched prefix with the alias path
# Request: /images/photo.jpg -> File: /data/photos/photo.jpg
location /images/ {
alias /data/photos/;
# Nginx looks for: /data/photos/ + photo.jpg
# IMPORTANT: alias path MUST end with / when location ends with /
}
# ---- Autoindex (Directory Listing) ----
location /downloads/ {
alias /var/www/files/;
autoindex on;
autoindex_exact_size off; # Show human-readable sizes (KB, MB)
autoindex_localtime on; # Show local time instead of UTC
autoindex_format html; # html, xml, json, or jsonp
}
# ---- Cache Headers for Static Assets ----
# Hashed filenames (e.g., app.a1b2c3.js) -- cache forever
location ~* \.[0-9a-f]{8,}\.(css|js)$ {
root /var/www/app;
expires max;
add_header Cache-Control "public, immutable";
access_log off;
}
# Regular static assets -- cache for 30 days
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|avif)$ {
root /var/www/app;
expires 30d;
add_header Cache-Control "public";
access_log off;
}
# Web fonts -- cache for 1 year, allow cross-origin
location ~* \.(woff|woff2|ttf|eot|otf)$ {
root /var/www/app;
expires 1y;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*";
access_log off;
}
# HTML files -- short cache, always revalidate
location ~* \.html$ {
root /var/www/app;
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# ---- Serving Pre-compressed Files ----
location /assets/ {
root /var/www;
# Try serving .br (Brotli) first, then .gz (Gzip), then original
gzip_static on; # Serve .gz if it exists
# brotli_static on; # Requires ngx_brotli module
expires 1y;
add_header Cache-Control "public, immutable";
}8. Location 块中的重写规则
rewrite 指令使用正则修改请求 URI,而 return 发送即时响应。理解何时使用每个指令,以及 last、break、permanent 和 redirect 标志之间的区别,对于 URL 管理至关重要。
# ---- return vs rewrite ----
# RETURN: simple, fast, sends immediate response (preferred when possible)
# REWRITE: regex-based URI modification, more powerful but slower
# ---- return examples ----
# 301 Permanent redirect (SEO-friendly, browsers cache this)
location = /old-page {
return 301 /new-page;
}
# 302 Temporary redirect (not cached by browsers)
location = /promo {
return 302 /summer-sale;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
# Redirect www to non-www
server {
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
# Return a custom response body
location /api/maintenance {
return 503 '{"status":"maintenance","retry_after":3600}';
add_header Content-Type application/json;
add_header Retry-After 3600;
}
# ---- rewrite examples ----
# Syntax: rewrite regex replacement [flag];
# Flags: last | break | redirect (302) | permanent (301)
# Rewrite: /blog/2024/my-post -> /posts?year=2024&slug=my-post
location /blog/ {
rewrite ^/blog/([0-9]{4})/(.+)$ /posts?year=$1&slug=$2 last;
}
# Remove trailing slashes
location ~ ^(.+)/$ {
rewrite ^(.+)/$ $1 permanent;
}
# Add trailing slashes to directories
location ~ ^/[^.]*[^/]$ {
rewrite ^(.*)$ $1/ permanent;
}
# Clean URLs: /about -> /about.html
location / {
rewrite ^/([^.]+)$ /$1.html last;
}
# ---- last vs break ----
# last: stops current rewrite, restarts location matching with new URI
# break: stops current rewrite, continues processing in current location
# Example demonstrating the difference:
location /download/ {
# 'last' restarts location search -- may match a different location
rewrite ^/download/(.*)$ /files/$1 last;
}
location /internal/ {
# 'break' stays in this location block
rewrite ^/internal/(.*)$ /private/$1 break;
root /var/www;
# Serves /var/www/private/...
}9. 使用 Location 块进行速率限制
速率限制保护特定端点免受滥用。通过在目标 location 块内应用 limit_req,你可以为登录页面、API 端点和静态资源设置不同的速率限制。
# ---- Define rate limit zones (in http {} block) ----
# General pages: 10 requests/second per IP
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
# Login/auth endpoints: 5 requests/minute per IP (strict)
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/m;
# API endpoints: 30 requests/second per IP
limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s;
# File uploads: 2 requests/second per IP
limit_req_zone $binary_remote_addr zone=upload:10m rate=2r/s;
# ---- Apply rate limits in location blocks ----
server {
listen 80;
server_name app.example.com;
# Custom 429 error response
error_page 429 = @rate_limited;
location @rate_limited {
default_type application/json;
return 429 '{"error":"rate_limit_exceeded","message":"Too many requests. Please retry later.","retry_after":60}';
}
# General pages with generous burst
location / {
limit_req zone=general burst=20 nodelay;
limit_req_status 429;
proxy_pass http://frontend:3000;
}
# Strict rate limiting for auth endpoints
location /api/auth/ {
limit_req zone=auth burst=3 nodelay;
limit_req_status 429;
proxy_pass http://auth-service:4000;
}
# API with moderate burst
location /api/ {
limit_req zone=api burst=50 nodelay;
limit_req_status 429;
proxy_pass http://api-service:4000;
}
# Upload endpoint with strict limits
location /api/upload {
limit_req zone=upload burst=5; # No nodelay: excess requests are delayed
limit_req_status 429;
client_max_body_size 100M;
proxy_pass http://upload-service:4000;
}
# No rate limiting for static assets
location ~* \.(css|js|jpg|png|gif|ico|svg|woff2)$ {
# No limit_req here -- static assets should always be fast
root /var/www/static;
expires 30d;
access_log off;
}
# burst vs nodelay explained:
# burst=20 nodelay -> Accept 20 extra requests instantly, reject beyond that
# burst=20 -> Accept 20 extra requests but delay them to match the rate
# burst=20 delay=10 -> First 10 excess processed immediately, rest delayed
}10. Location 块中的安全头
在 location 块中添加安全头让你可以精细控制哪些头应用于哪些路径。这很重要,因为 API、静态资源和 HTML 页面通常需要不同的安全策略。
# ---- Security headers for HTML pages ----
location / {
proxy_pass http://frontend:3000;
# Prevent clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# Block MIME-type sniffing
add_header X-Content-Type-Options "nosniff" always;
# Control referrer information
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy (customize per application)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self';" always;
# HSTS: force HTTPS for 2 years
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Permissions Policy
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()" always;
}
# ---- Relaxed headers for API endpoints ----
location /api/ {
proxy_pass http://api-backend:4000;
# APIs need CORS, not CSP
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
# CORS headers
add_header Access-Control-Allow-Origin "https://example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
add_header Access-Control-Max-Age 86400 always;
# Handle preflight requests
if ($request_method = OPTIONS) {
return 204;
}
}
# ---- Minimal headers for static assets ----
location ~* \.(css|js|jpg|png|gif|svg|woff2|ico)$ {
root /var/www/static;
expires 30d;
add_header Cache-Control "public, immutable";
# Allow fonts to be loaded cross-origin
add_header Access-Control-Allow-Origin "*";
# No CSP or HSTS needed for static assets
# (browsers apply the headers from the HTML page)
}
# ---- Block sensitive paths ----
# Block hidden files (.git, .env, .htaccess, .DS_Store)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
return 404;
}
# Block common sensitive files
location ~* /(composer\.json|package\.json|package-lock\.json|yarn\.lock|\.env.*|Makefile|Dockerfile|docker-compose\.ya?ml)$ {
deny all;
return 404;
}11. 常见实际应用模式
以下是针对最流行框架和部署场景的完整、经过生产测试的 location 块配置。
# ========================================
# WordPress Configuration
# ========================================
server {
listen 80;
server_name wordpress.example.com;
root /var/www/wordpress;
index index.php;
# WordPress permalinks
location / {
try_files $uri $uri/ /index.php?$args;
}
# PHP processing
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_intercept_errors on;
}
# Block access to wp-config.php
location = /wp-config.php {
deny all;
}
# Block xmlrpc.php (common attack vector)
location = /xmlrpc.php {
deny all;
return 403;
}
# Cache static assets
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}
}
# ========================================
# Next.js (Node.js backend) Configuration
# ========================================
server {
listen 80;
server_name nextjs.example.com;
# Proxy everything to Next.js server
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Serve Next.js static files directly (/_next/static/)
location /_next/static/ {
alias /var/www/nextjs/.next/static/;
expires 365d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Serve public directory directly
location /public/ {
alias /var/www/nextjs/public/;
expires 30d;
access_log off;
}
}
# ========================================
# React SPA (Static Build) Configuration
# ========================================
server {
listen 80;
server_name react.example.com;
root /var/www/react/build;
# SPA fallback -- all routes go to index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache hashed assets forever
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Do NOT cache index.html
location = /index.html {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# API proxy to backend
location /api/ {
proxy_pass http://127.0.0.1:4000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# ========================================
# API + Static Files (Microservice Style)
# ========================================
server {
listen 80;
server_name app.example.com;
# Frontend (SPA)
location / {
root /var/www/frontend/dist;
try_files $uri $uri/ /index.html;
}
# API v1 -> Service A
location /api/v1/ {
proxy_pass http://service-a:3001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# API v2 -> Service B
location /api/v2/ {
proxy_pass http://service-b:3002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Uploads with size limit
location /api/upload {
client_max_body_size 50M;
proxy_pass http://upload-service:3003;
proxy_set_header Host $host;
}
# WebSocket endpoint
location /ws {
proxy_pass http://ws-service:3004;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Shared static assets
location /assets/ {
alias /var/www/shared-assets/;
expires 30d;
add_header Cache-Control "public";
}
}12. 调试 Location 块
当你的 location 块行为不符合预期时,以下工具和技术将帮助你快速识别问题。
# ---- Step 1: Always Test Before Reloading ----
# Check configuration syntax
nginx -t
# Output: nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# Test and show the full resolved configuration
nginx -T
# ---- Step 2: Enable Debug Logging ----
# In nginx.conf or server block:
error_log /var/log/nginx/error.log debug;
# Or enable debug for specific IPs only (less performance impact):
events {
debug_connection 192.168.1.100;
debug_connection 10.0.0.0/24;
}
# ---- Step 3: Add Debug Headers to Identify Which Location Matched ----
# Add this to each location block during debugging:
location = / {
add_header X-Debug-Location "exact-root" always;
# ... your config ...
}
location ^~ /static/ {
add_header X-Debug-Location "prefix-priority-static" always;
# ... your config ...
}
location ~* \.(css|js)$ {
add_header X-Debug-Location "regex-css-js" always;
# ... your config ...
}
location / {
add_header X-Debug-Location "default-prefix" always;
# ... your config ...
}
# ---- Step 4: Test with curl ----
# Check which location handled the request by inspecting headers:
curl -I https://example.com/
curl -I https://example.com/static/style.css
curl -I https://example.com/api/users
# Check response headers:
curl -sI https://example.com/ | grep X-Debug-Location
# Output: X-Debug-Location: exact-root
# Test redirect behavior:
curl -L -v https://example.com/old-page 2>&1 | grep "< HTTP\|< Location"
# ---- Step 5: Monitor Access and Error Logs ----
# Watch access log in real-time:
tail -f /var/log/nginx/access.log
# Filter for specific URI:
tail -f /var/log/nginx/access.log | grep "/api/"
# Watch error log for config issues:
tail -f /var/log/nginx/error.log
# ---- Step 6: Common Issues and Fixes ----
# Issue: "rewrite or internal redirection cycle"
# Cause: location blocks redirect to each other infinitely
# Fix: Check your rewrite rules and try_files for circular references
# Issue: "could not build server_names_hash"
# Fix: Increase hash bucket size in http {} block:
# server_names_hash_bucket_size 128;
# Issue: 403 Forbidden on static files
# Fix: Check file permissions and ensure nginx user can read:
# ls -la /var/www/
# chown -R nginx:nginx /var/www/
# chmod -R 755 /var/www/
# Issue: Regex location not matching
# Debug: Test your regex pattern at regex101.com
# Remember: Nginx uses PCRE, not JavaScript or Python regex
# Remember: Backslashes must be escaped in nginx.conf: \. not .
# ---- Reload After Fixing ----
nginx -t && nginx -s reload常见问题
Nginx 中 location = / 和 location / 有什么区别?
"location = /" 仅匹配精确的根路径(/),不匹配其他任何内容。这是最快的匹配。"location /" 是前缀匹配,匹配所有 URI,因为所有 URI 都以 / 开头。它充当默认的全部捕获 location。实际使用中,"location = /" 用于首页特定配置,"location /" 用作其他所有内容的回退。
为什么我的正则 location 块不匹配?Nginx location 优先级如何工作?
Nginx 按以下严格顺序评估 location:(1) 精确匹配 = 立即停止,(2) 带 ^~ 的最长前缀匹配立即停止,(3) 正则块 ~ 和 ~* 按配置文件顺序检查,第一个匹配获胜,(4) 如果没有正则匹配,使用最长的前缀匹配。如果你的正则不匹配,检查是否有 ^~ 前缀块先拦截了请求,或者文件中更早出现的另一个正则是否匹配了。
Nginx location 块中 root 和 alias 有什么区别?
使用 "root" 时,Nginx 将完整 URI 附加到 root 路径。因此 "location /images/ { root /data; }" 对于 URI /images/photo.jpg 提供 /data/images/photo.jpg。使用 "alias" 时,Nginx 用 alias 路径替换匹配的 location 前缀。因此 "location /images/ { alias /data/photos/; }" 对于相同的 URI 提供 /data/photos/photo.jpg。当文件系统路径不反映 URL 结构时使用 alias。
如何让 Nginx location 块与 React 或 Next.js SPA 配合工作?
对于 SPA,你需要 try_files 回退到 index.html 以支持客户端路由:"location / { try_files $uri $uri/ /index.html; }"。对于带 Node.js 后端的 Next.js,代理所有请求:"location / { proxy_pass http://localhost:3000; }"。对于 Next.js 静态导出,使用 SPA 模式。为 API 路由和静态资源添加单独的 location 块并设置适当的缓存头。
我可以在一个 Nginx location 块中使用多个正则模式吗?
不可以,每个 location 块只接受一个模式。但你可以在单个正则模式中使用正则交替(| 运算符)来匹配多个模式:"location ~* \.(jpg|png|gif|webp)$ { ... }"。这匹配以 .jpg、.png、.gif 或 .webp 结尾的任何 URI。对于完全不同的模式,创建单独的 location 块。
掌握 Nginx location 块是构建快速、安全、可维护的 Web 服务器配置的关键。简单场景使用精确匹配和前缀匹配,仅在需要时使用正则,重载前始终用 "nginx -t" 测试。使用下方工具生成和测试你的配置。