You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nginx中对匹配到的路径段进行URL编码?

Nginx实现路径参数URL编码后反向代理的方案

问题描述

我有一个用于获取项目内文件所有者的API端点,需要将请求URL中的项目路径和文件名部分做URL编码后,反向代理到后端服务器。期望的配置逻辑和请求示例如下:

目标配置逻辑

location ~ /api/project_path/(.*)/file/(.*)/owner {
  # 将$1编码为$encoded_projectpath
  # 将$2编码为$encoded_file
  proxy_pass http://backend/$encoded_projectpath/$encoded_file/owner;
}

请求示例

curl http://localhost/api/project_path/my/project/path/file/my/file/name/owner
# 期望Nginx反向代理到:http://backend/my%2Fproject%2Fpath/my%2Ffile%2Fname/owner

请问Nginx是否有内置方法实现该需求?我自己尝试了njs方案,但该方案无法扩展到其他端点,代码如下:

现有njs脚本(http.js)

function urlencode_projectpath(r) {
  let pattern = "(?<=/api/project_path/)(.*)/file/(.*)/upload_url"
  let url = r.variables.request_uri
  let project_path = url.match(pattern)[0]
  
  return encodeURIComponent(project_path)
}

function urlencode_file(r) {
  let pattern = "(?<=/api/project_path/)(.*)/file/(.*)/upload_url"
  let url = r.variables.request_uri
  let file= url.match(pattern)[1]
  
  return encodeURIComponent(file)
}
export default { urlencode_projectpath, urlencode_file }

现有Nginx配置片段

http {
  js_import /etc/nginx/conf.d/http.js;
  js_set $encoded_projectpath http.urlencode_projectpath;
  js_set $encoded_file http.urlencode_file;
  server {
    location ~ /api/project_path/(.*)/file/(.*)/owner {
      proxy_pass http://backend/$encoded_projectpath/$encoded_file/owner;
    }
  }
}

解决方案

一、第三方模块实现(依赖ngx_set_misc)

Nginx官方核心模块没有内置的URL编码函数,但可以通过第三方模块ngx_set_misc提供的set_escape_uri指令快速实现。

配置示例

location ~ /api/project_path/(.*)/file/(.*)/owner {
    # 对捕获的分组$1、$2分别做URL编码
    set_escape_uri $encoded_projectpath $1;
    set_escape_uri $encoded_file $2;
    proxy_pass http://backend/$encoded_projectpath/$encoded_file/owner;
}

注意:需要先安装ngx_set_misc模块(可通过OpenResty直接获取,或自行编译到Nginx)。

二、可扩展的njs实现方案

如果不想依赖第三方模块,可以改进njs脚本,使其支持通用URL编码,并轻松扩展到其他端点。

方案1:通用编码函数+Nginx变量传递

这种方式无需修改njs脚本即可扩展到任意端点:

改进后的njs脚本(http.js)
// 通用URL编码函数,接收Nginx传递的待编码内容
function urlEncode(r) {
    const input = r.variables.input_to_encode;
    return input ? encodeURIComponent(input) : '';
}

export default { urlEncode };
对应的Nginx配置
http {
    js_import /etc/nginx/conf.d/http.js;
    # 定义通用编码变量
    js_set $encoded_input http.urlEncode;

    server {
        # 文件所有者端点
        location ~ /api/project_path/(.*)/file/(.*)/owner {
            # 传递$1给编码函数,得到编码后的项目路径
            set $input_to_encode $1;
            set $encoded_projectpath $encoded_input;
            # 传递$2给编码函数,得到编码后的文件名
            set $input_to_encode $2;
            set $encoded_file $encoded_input;
            proxy_pass http://backend/$encoded_projectpath/$encoded_file/owner;
        }

        # 扩展:新增upload_url端点
        location ~ /api/project_path/(.*)/file/(.*)/upload_url {
            set $input_to_encode $1;
            set $encoded_projectpath $encoded_input;
            set $input_to_encode $2;
            set $encoded_file $encoded_input;
            proxy_pass http://backend/$encoded_projectpath/$encoded_file/upload_url;
        }

        # 扩展:其他任意结构的端点
        location ~ /api/another/(.*)/resource/(.*)/info {
            set $input_to_encode $1;
            set $encoded_path $encoded_input;
            set $input_to_encode $2;
            set $encoded_resource $encoded_input;
            proxy_pass http://backend/$encoded_path/$encoded_resource/info;
        }
    }
}

方案2:端点专属提取函数(适合固定结构的API)

如果你的API端点结构固定,可以在njs中编写对应提取逻辑,避免重复配置:

改进后的njs脚本(http.js)
// 提取并编码项目路径(匹配owner端点)
function encodeOwnerProjectPath(r) {
    const match = r.variables.request_uri.match(/^\/api\/project_path\/(.*)\/file\/.*\/owner$/);
    return match ? encodeURIComponent(match[1]) : '';
}

// 提取并编码文件名(匹配owner端点)
function encodeOwnerFileName(r) {
    const match = r.variables.request_uri.match(/^\/api\/project_path\/.*\/file\/(.*)\/owner$/);
    return match ? encodeURIComponent(match[1]) : '';
}

// 扩展:提取并编码upload_url端点的参数
function encodeUploadProjectPath(r) {
    const match = r.variables.request_uri.match(/^\/api\/project_path\/(.*)\/file\/.*\/upload_url$/);
    return match ? encodeURIComponent(match[1]) : '';
}

function encodeUploadFileName(r) {
    const match = r.variables.request_uri.match(/^\/api\/project_path\/.*\/file\/(.*)\/upload_url$/);
    return match ? encodeURIComponent(match[1]) : '';
}

export default { encodeOwnerProjectPath, encodeOwnerFileName, encodeUploadProjectPath, encodeUploadFileName };
对应的Nginx配置
http {
    js_import /etc/nginx/conf.d/http.js;
    # 为每个端点的参数定义编码变量
    js_set $encoded_owner_project http.encodeOwnerProjectPath;
    js_set $encoded_owner_file http.encodeOwnerFileName;
    js_set $encoded_upload_project http.encodeUploadProjectPath;
    js_set $encoded_upload_file http.encodeUploadFileName;

    server {
        location ~ /api/project_path/(.*)/file/(.*)/owner {
            proxy_pass http://backend/$encoded_owner_project/$encoded_owner_file/owner;
        }

        location ~ /api/project_path/(.*)/file/(.*)/upload_url {
            proxy_pass http://backend/$encoded_upload_project/$encoded_upload_file/upload_url;
        }
    }
}

内容的提问来源于stack exchange,提问作者Chiamin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:52:04