Nginx location 匹配与路径映射详解

location 只负责匹配 URL,真正决定怎么转发/映射的是 proxy_passrootalias 等指令


一、先理解:location 匹配的是什么?

text
location /api/ {
    # 这里的 /api/ 匹配的是 URL 中的路径部分
    # 例如:http://example.com/api/users → 匹配 /api/users
}

关键点:location 匹配的是 URI 的路径部分(不包括域名、协议、参数)。


二、静态文件服务:root vs alias

1. root(拼接模式)

text
location /images/ {
    root /var/www;
}

映射规则root 路径 + 请求 URI = 实际文件路径

请求 URL 实际查找文件
/images/logo.png /var/www/images/logo.png
/images/photo/1.jpg /var/www/images/photo/1.jpg

特点:root 会保留 location 匹配的路径作为子目录。


2. alias(替换模式)

text
location /images/ {
    alias /var/www/static/;
}

映射规则alias 路径 + (请求 URI 去掉 location 匹配部分) = 实际文件路径

请求 URL 实际查找文件
/images/logo.png /var/www/static/logo.png
/images/photo/1.jpg /var/www/static/photo/1.jpg

特点:alias 会剥离 location 匹配的路径前缀。


root vs alias 对比示例

text
# 假设请求 /static/css/style.css

# 使用 root
location /static/ {
    root /var/www;
}
# 实际查找:/var/www/static/css/style.css ✅

# 使用 alias
location /static/ {
    alias /var/www/assets/;
}
# 实际查找:/var/www/assets/css/style.css ✅

# 常见错误:alias 末尾忘记加斜杠
location /static/ {
    alias /var/www/assets;  # ❌ 错误!
}
# 实际查找:/var/www/assetscss/style.css (拼接错误)

三、反向代理:proxy_pass 的路径映射

核心规则:proxy_pass 是否带斜杠 / 决定路径如何处理

1. proxy_pass 不带斜杠(原样转发)

text
location /api/ {
    proxy_pass http://backend;  # 注意:没有斜杠
}

规则:保留 location 匹配的部分

请求 URL 转发到后端
/api/users http://backend/api/users
/api/order/123 http://backend/api/order/123

2. proxy_pass 带斜杠(剥离前缀)

text
location /api/ {
    proxy_pass http://backend/;  # 注意:有斜杠
}

规则:去掉 location 匹配的前缀

请求 URL 转发到后端
/api/users http://backend/users
/api/order/123 http://backend/order/123

3. 更精确的控制(使用变量)

text
location /api/ {
    # 完全重写路径
    rewrite ^/api/(.*)$ /v2/$1 break;
    proxy_pass http://backend;
}
# /api/users → /v2/users

# 或使用 $uri 变量
location /api/ {
    proxy_pass http://backend$request_uri;
}
# 原样转发(包含参数)

四、完整示例:常见场景

场景1:静态文件服务

text
server {
    listen 80;
    server_name example.com;
    
    # 方案1:使用 root(保留路径)
    location /static/ {
        root /var/www;
        # /static/css/style.css → /var/www/static/css/style.css
    }
    
    # 方案2:使用 alias(剥离路径)
    location /assets/ {
        alias /var/www/public/;
        # /assets/logo.png → /var/www/public/logo.png
    }
    
    # 方案3:精确匹配首页
    location = / {
        root /var/www/html;
        index index.html;
    }
}

场景2:API 网关(路径重写)

text
upstream backend_api {
    server 192.168.1.10:8080;
}

server {
    listen 80;
    
    # 需求1:/api/users → 转发到 /users(去掉 /api)
    location /api/ {
        proxy_pass http://backend_api/;  # 注意斜杠
        proxy_set_header Host $host;
    }
    
    # 需求2:/v1/users → 转发到 /v1/users(保留原样)
    location /v1/ {
        proxy_pass http://backend_api;  # 无斜杠
    }
    
    # 需求3:/old/xxx → 转发到 /new/xxx(重写)
    location /old/ {
        rewrite ^/old/(.*)$ /new/$1 break;
        proxy_pass http://backend_api;
    }
    
    # 需求4:完全自定义转发路径
    location /legacy/ {
        proxy_pass http://backend_api/custom/path/;
        # /legacy/user → /custom/path/user
    }
}

场景3:前后端分离项目

text
server {
    listen 80;
    server_name myapp.com;
    
    # 前端:根路径返回静态 HTML
    location / {
        root /var/www/frontend/dist;
        try_files $uri $uri/ /index.html;  # SPA 路由支持
    }
    
    # 后端 API:转发到 Node.js
    location /api/ {
        proxy_pass http://localhost:3000/;  # 去掉 /api 前缀
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    # 文件上传:特殊处理
    location /upload/ {
        proxy_pass http://localhost:3000/files/;  # 改变路径
        client_max_body_size 50M;
    }
    
    # 静态资源:直接返回(加缓存)
    location ~* \.(js|css|png|jpg)$ {
        root /var/www/frontend/dist;
        expires 30d;
    }
}

五、测试映射规则的方法

1. 使用 echo 模块测试(最直接)

text
location /api/ {
    # 回显转发路径
    add_header Content-Type text/plain;
    return 200 "Will proxy to: http://backend$request_uri\n";
}

2. 查看 Nginx 错误日志

text
location /api/ {
    # 开启调试日志
    proxy_pass http://backend/;
    error_log /var/log/nginx/debug.log debug;
}

3. 使用 curl 测试

bash
# 查看实际转发的请求
curl -v http://localhost/api/users

# 在后端服务器抓包
tcpdump -i lo -A port 8080

六、核心规则速查表

配置 请求 实际转发/查找 说明
root /var/www; /static/a.css /var/www/static/a.css root 保留路径
alias /var/www/; /static/a.css /var/www/a.css alias 剥离前缀
proxy_pass http://backend; /api/users http://backend/api/users 保留原路径
proxy_pass http://backend/; /api/users http://backend/users 剥离 location 前缀
proxy_pass http://backend/v2; /api/users http://backend/v2users ⚠️ 陷阱(缺斜杠)
proxy_pass http://backend/v2/; /api/users http://backend/v2/users 完全替换路径

七、常见陷阱与最佳实践

❌ 错误示例

text
# 陷阱1:alias 缺少末尾斜杠
location /static/ {
    alias /var/www/static;  # ❌ 
    # /static/a.css → /var/www/statica.css
}

# 陷阱2:proxy_pass 路径混淆
location /api {
    proxy_pass http://backend/;  # ⚠️ 注意 /api 没有尾斜杠
    # /api/users → http://backend//users(双斜杠)
}

# 陷阱3:location 和 proxy_pass 斜杠不匹配
location /api/ {
    proxy_pass http://backend;  # ⚠️ /api/ 有斜杠,backend 无斜杠
    # /api/users → http://backend/api/users ✅ 实际正确,但容易混淆
}

✅ 最佳实践

text
# 1. 统一风格:location 和 proxy_pass 都带斜杠
location /api/ {
    proxy_pass http://backend/;  # 剥离前缀
}

# 2. 或统一不带(保留原样)
location /api {
    proxy_pass http://backend;   # 保留前缀
}

# 3. 使用变量明确控制
location /api/ {
    set $backend_url "http://backend";
    proxy_pass $backend_url$request_uri;  # 明确意图
}

# 4. 复杂重写用 rewrite
location /legacy/ {
    rewrite ^/legacy/(.*)$ /api/v2/$1 break;
    proxy_pass http://backend;
}

八、记忆口诀

root 拼接保留路径,alias 替换要剥去
proxy_pass 带斜杠剥离,不带斜杠原样转
location 尾斜杠要小心,两边对齐不出错

快速判断规则:

  1. root:请求路径追加到 root 后面
  2. alias:请求路径去掉 location 后再追加
  3. proxy_pass 无斜杠:转发原路径
  4. proxy_pass 有斜杠:转发去掉 location 匹配部分后的路径