DevToolBox免费
博客

Nginx location 块与正则表达式指南

10 分钟阅读作者 DevToolBox

nginx location 块是控制 Nginx 处理传入请求的最关键指令。它决定哪个配置适用于给定的 URL,使你能够精确地路由流量、提供文件、代理到后端以及应用安全规则。本全面指南涵盖了 location 块语法、正则修饰符、匹配优先级以及 30+ 个生产可用的示例,你可以立即复制和使用。

使用我们的 Nginx 配置生成器生成 Nginx 配置使用我们的正则测试器测试你的正则表达式

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 配置最重要的概念。每个修饰符改变了模式的解释方式,并影响匹配的优先级。

修饰符类型说明优先级
=ExactExact URI match, stops immediately1 (highest)
^~Prefix priorityPrefix match, skips regex if matched2
~Regex (case-sensitive)PCRE regex, first match in file order wins3
~*Regex (case-insensitive)PCRE regex, case-insensitive3
(none)PrefixLongest prefix match, can be overridden by regex4 (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 擅长提供静态文件。理解 rootalias 的区别,以及如何设置正确的缓存头,对于性能优化至关重要。

# ---- 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 发送即时响应。理解何时使用每个指令,以及 lastbreakpermanentredirect 标志之间的区别,对于 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" 测试。使用下方工具生成和测试你的配置。

使用 Nginx 配置生成器生成配置使用正则测试器测试你的正则表达式
𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

NXNginx Config Generator.*Regex Tester.ht.htaccess Generator

相关文章

Nginx 配置示例:反向代理、SSL 和静态站点

生产级 Nginx 配置示例:反向代理、SSL/TLS、静态文件服务、负载均衡和安全头。

Regex 速查表:正则表达式完全参考指南

全面的正则表达式速查表:语法、字符类、量词、前瞻断言,以及 JavaScript、Python、Go 中的实用模式。