nginx 中文url rewrite 404

2023-10-27

原料

nginx --with-debug或openresty

背景

项目中有用户图片库需求,允许用户自定义文件夹,然后上传图片到该文件夹。 当用户自定义的文件夹为中文或者访问url中包含中文时,资源无法访问,返回404的状态码

分析过程

排除系统编码问题。

[root@slave2 ~]# locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
[root@slave2 ~]# env|grep LANG
LANG=en_US.UTF-8

nginx编码 当我们在网页中不指定编码类型时,默认为gbk编码.可以通过在nginx中添加默认的编码方式

Syntax:     charset charset | off;
Default:    charset off;
Context:    http, server, location, if in location

此处设置的charset会在response header中Content-Type:text/html;charset=utf-8来体现 或者在网页中指定编码类型:<meta http-equiv="content-type" content="text/html;charset=utf-8">

注:网上大多教程为页面中文乱码问题。并不是此处描述的url中文404,请注意区分。

首先来看nginx 配置

server{
    listen 8089;
    server_name 172.19.23.208;
    if ($uri ~*  ^/(.+?)/(.*) ) {
        set $domain $1;
        set $user_uri  /$2;
        rewrite ^(.*)$  $user_uri last;
    }
    location / {
        proxy_set_header Host $domain;
        proxy_pass            http://127.0.0.1:8087;

    }
}

开启nginx debug模式,源码包安装时加上--with-debug参数,或者使用oenresty-debug包。 构造访问url:http://172.19.23.208:8089/www.test.com/猴哥/a.html 发起访问,同时我们侦测nginx debug日志。 这里选取主要的日志如下

  1      2017/06/22 17:16:41 [debug] 8389#0: *44 http request line: "GET /www.test.com/%E7%8C%B4%E5%93%A5/a.html HTTP/1.1"
  2      2017/06/22 17:16:41 [debug] 8389#0: *44 http uri: "/www.test.com/猴哥/a.html"
  3      2017/06/22 17:16:41 [debug] 8389#0: *44 http script var: "/www.test.com/猴哥/a.html"
  4      2017/06/22 17:16:41 [debug] 8389#0: *44 http script regex: "^/(.+?)/(.*)"
  5      2017/06/22 17:16:41 [notice] 8389#0: *44 "^/(.+?)/(.*)" matches "/www.test.com/猴哥/a.html", client: 172.19.23.33, server: 172.19.23.208, request: "GET /www.test.com/%E7%8C%B4%E5%93%A5/a.html HTTP/1.1", host: "172.19.23.208:8089"
  6      2017/06/22 17:16:41 [debug] 8389#0: *44 http script if
  7      2017/06/22 17:16:41 [debug] 8389#0: *44 http script capture: "www.test.com"
  8      2017/06/22 17:16:41 [debug] 8389#0: *44 http script set $domain
  9      2017/06/22 17:16:41 [debug] 8389#0: *44 http script copy: "/"
 10      2017/06/22 17:16:41 [debug] 8389#0: *44 http script capture: "%E7%8C%B4%E5%93%A5/a.html"
 11      2017/06/22 17:16:41 [debug] 8389#0: *44 http script set $user_uri
 12      2017/06/22 17:16:41 [debug] 8389#0: *44 http script regex: "^(.*)$"
 13      2017/06/22 17:16:41 [notice] 8389#0: *44 "^(.*)$" matches "/www.test.com/猴哥/a.html", client: 172.19.23.33, server: 172.19.23.208, request: "GET /www.test.com/%E7%8C%B4%E5%93%A5/a.html HTTP/1.1", host: "172.19.23.208:8089"
 14      2017/06/22 17:16:41 [debug] 8389#0: *44 http script var: "/%E7%8C%B4%E5%93%A5/a.html"
 15      2017/06/22 17:16:41 [debug] 8389#0: *44 http script regex end
 16      2017/06/22 17:16:41 [notice] 8389#0: *44 rewritten data: "/%E7%8C%B4%E5%93%A5/a.html", args: "", client: 172.19.23.33, server: 172.19.23.208, request: "GET /www.test.com/%E7%8C%B4%E5%93%A5/a.html HTTP/1.1", host: "172.19.23.208:8089"

1 原始请求
2 对应nginx变量$uri,可以看出$uri为解码过后的值
3 http脚本变量值"/www.test.com/猴哥/a.html" 此处为$uri
4 对应nginx配置文件中if后的正则表达式
5 测试if条件,结果为matches
6 进入if块
7,8,9,10,11 nginx变量捕获到值并进行set,注意在'https://github.com/nginx/nginx/blob/master/src/http/ngx_http_script.c', 函数ngx_http_script_copy_capture_code中

    if ((e->is_args || e->quote) && (e->request->quoted_uri || e->request->plus_in_uri))
        {
            e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
                                               cap[n + 1] - cap[n],
                                               NGX_ESCAPE_ARGS);
        } else {
            e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
        }
确定了哪些条件会ngx_escape_uri,很明显中文编码在这些条件中。
注意此处我们得到了编码过后的$user_uri="%E7%8C%B4%E5%93%A5/a.html"
12,13,14,15,16
rewrite指令匹配然后使用已捕获的变量进行rewrite

梳理下:用户在浏览器中访问http://172.19.23.208:8089/www.test.com/猴哥/a.html ,由于url中有中文,浏览器自动将其就行urlencode(用户可见与真实url已经存在差异),这是第一次,然后 nginx得到用户请求后开始uri解析,$request_uri="/www.test.com/%E7%8C%B4%E5%93%A5/a.html" 该变量是不会变化的。$uri="/www.test.com/猴哥/a.html" 该变量表示当前请求的uri,在发生内部跳转,或者索引文件时可以被改写。 然后通过if的匹配,就行变量的捕获这些这都正常。但是set指令对应逻辑中有ngx_escape_uri操作,也就是上面说到的7-11步。最后进行rewrite操作,注意此处rewrite的是$user_uri(/%E7%8C%B4%E5%93%A5/a.html)的值。此处有两次内部跳转,一次为rewrite,第二次为proxy_pass 第二次proxy_pass可以理解为正常用户通过浏览器访问(进行一次urlencode("%E7%8C%B4%E5%93%A5"-->'%25E7%258C%25B4%25E5%2593%25A5')),nginx收到请求为 "GET /%25E7%258C%25B4%25E5%2593%25A5/a.html?b=2 HTTP/1.0", 后面的逻辑便和之前相同。如果该资源为本地文件,nginx使用$uri(/%E7%8C%B4%E5%93%A5/a.html)进行文件查找,结果便为404。其他proxy_pass逻辑也会出现404问题。

(需要说明nginx中$request_uri和$uri,前者代表包含参数且未进过解码的链接后者为解码后未包含参数的链接,顺便提一点,如果我们在日志中打印nginx变量$request_uri和$uri会的到相同的值,这是因为在nginx的log模块中也进行了escape操作。两者的值可以通过nginx debug日志看出差异。)

解决方案

既然此种需求问题在set指令时对url就行了ngx_escape_uri,我们只需在其之后就行unescape_uri便可。

server{
    listen 8089;
    server_name 172.19.23.208;
    if ($uri ~*  ^/(.+?)/(.*) ) {
        set $domain $1;
        set $user_uri  /$2;
        set_by_lua_block $new_url {
                return ngx.unescape_uri(ngx.var.user_uri)
            }
        rewrite ^(.*)$  $new_url last;
    }
    location / {
        proxy_set_header Host $domain;
        proxy_pass            http://127.0.0.1:8087;

    }
}

这里使用openresty的ngx.unescape_uri方法实现 或者使用第三方模块ngx_set_misc 的set_unescape_uri。

set_unescape_uri $new_url $user_uri;

转载于:https://my.oschina.net/monkeyzhu/blog/997815

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

nginx 中文url rewrite 404 的相关文章

  • Java 中的“Lambdifying”scala 函数

    使用Java和Apache Spark 已用Scala重写 面对旧的API方法 org apache spark rdd JdbcRDD构造函数 其参数为 AbstractFunction1 abstract class AbstractF
  • Pandas Dataframe 中 bool 值的条件前向填充

    问题 如何转发 fill boolTruepandas 数据框中的值 如果是当天的第一个条目 True 到一天结束时 请参阅以下示例和所需的输出 Data import pandas as pd import numpy as np df
  • 以编程方式在java的resources/source文件夹中创建文件?

    我有两个资源文件夹 src 这是我的 java 文件 资源 这是我的资源文件 图像 properties 组织在文件夹 包 中 有没有办法以编程方式在该资源文件夹中添加另一个 properties 文件 我尝试过这样的事情 public s
  • 当玩家触摸屏幕一侧时,如何让 pygame 发出警告?

    我使用 pygame 创建了一个游戏 当玩家触摸屏幕一侧时 我想让 pygame 给出类似 你不能触摸屏幕两侧 的错误 我尝试在互联网上搜索 但没有找到任何好的结果 我想过在屏幕外添加一个方块 当玩家触摸该方块时 它会发出警告 但这花了很长
  • 欧洲中部时间 14 日 3 月 30 日星期五 00:00:00 至 日/月/年

    我尝试解析格式日期Fri Mar 30 00 00 00 CET 14至 日 月 年 这是我的代码 SimpleDateFormat formatter new SimpleDateFormat dd MM yyyy System out
  • 如何在 Django 中使用并发进程记录到单个文件而不使用独占锁

    给定一个在多个服务器上同时执行的 Django 应用程序 该应用程序如何记录到单个共享日志文件 在网络共享中 而不保持该文件以独占模式永久打开 当您想要利用日志流时 这种情况适用于 Windows Azure 网站上托管的 Django 应
  • Python:计算字典的重复值

    我有一本字典如下 dictA unit1 test1 alpha unit1 test2 beta unit2 test1 alpha unit2 test2 gamma unit3 test1 delta unit3 test2 gamm
  • HQL Hibernate 内连接

    我怎样才能在 Hibernate 中编写这个 SQL 查询 我想使用 Hibernate 来创建查询 而不是创建数据库 SELECT FROM Employee e INNER JOIN Team t ON e Id team t Id t
  • 检查所有值是否作为字典中的键存在

    我有一个值列表和一本字典 我想确保列表中的每个值都作为字典中的键存在 目前我正在使用两组来确定字典中是否存在任何值 unmapped set foo set bar keys 有没有更Pythonic的方法来测试这个 感觉有点像黑客 您的方
  • glpk.LPX 向后兼容性?

    较新版本的glpk没有LPXapi 旧包需要它 我如何使用旧包 例如COBRA http opencobra sourceforge net openCOBRA Welcome html 与较新版本的glpk 注意COBRA适用于 MATL
  • 在python中,如何仅搜索所选子字符串之前的一个单词

    给定文本文件中的长行列表 我只想返回紧邻其前面的子字符串 例如单词狗 描述狗的单词 例如 假设有这些行包含狗 hotdog big dog is dogged dog spy with my dog brown dogs 在这种情况下 期望
  • 如何使用google colab在jupyter笔记本中显示GIF?

    我正在使用 google colab 想嵌入一个 gif 有谁知道如何做到这一点 我正在使用下面的代码 它并没有在笔记本中为 gif 制作动画 我希望笔记本是交互式的 这样人们就可以看到代码的动画效果 而无需运行它 我发现很多方法在 Goo
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • 为什么这个作业不起作用?

    我有课Results which extends ArrayList
  • 在 Python 类中动态定义实例字段

    我是 Python 新手 主要从事 Java 编程 我目前正在思考Python中的类是如何实例化的 我明白那个 init 就像Java中的构造函数 然而 有时 python 类没有 init 方法 在这种情况下我假设有一个默认构造函数 就像
  • FileOutputStream.close() 中的设备 ioctl 不合适

    我有一些代码可以使用以下命令将一些首选项保存到文件中FileOutputStream 这是我已经写了一千遍的标准代码 FileOutputStream out new FileOutputStream file try BufferedOu
  • hashcode 的默认实现为以相同方式构造的对象返回不同的值

    我在这里编写一个示例代码 public class Test private int i private int j public Test TODO Auto generated constructor stub public Test
  • Trie 数据结构 - Java [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 是否有任何库或文档 链接提供了在 java 中实现 Trie 数据结构的更多信息 任何帮助都会很棒 Thanks 你可以阅读Java特里树
  • 如何从 Maven 存储库引用本机 DLL?

    如果 JAR 附带 Maven 存储库中的本机 DLL 我需要在 pom xml 中放入什么才能将该 DLL 放入打包中 更具体地举个例子Jacob http search maven org artifactdetails 7Cnet s
  • 调整添加的绘制组件的大小和奇怪的摆动行为

    这个问题困扰了我好几天 我正在制作一个特殊的绘画程序 我制作了一个 JPanel 并添加了使用 Paint 方法绘制的自定义 jComponent 问题是 每当我调整窗口大小时 所有添加的组件都会 消失 或者只是不绘制 因此我最终会得到一个

随机推荐

  • 基于Lua的冒泡排序算法实现

    冒泡排序核心 比较相邻的元素 如果第一个比第二个大 就交换他们两个 对每一对相邻元素作同样的工作 从开始第一对到结尾的最后一对 这步做完后 最后的元素会是最大的数 针对所有的元素重复以上的步骤 除了最后一个 持续每次对越来越少的元素重复上面
  • win10安装ZED Python API以及解决python get_python_api.py报错

    Python 安装脚本位于 C Program Files x86 ZED SDK 中 运行如下命令 cd C Program Files x86 ZED SDK python get python api py 此时若出现如下报错 解决方
  • npm安装模块的 npm WARN root@1.0.0 No description 和 npm WARN root@1.0.0 No repository filed 的解决方法

    报错分析 是因为系统在给你配置package json文件的时候并没有帮你设置description字段和存储库字段 需要我们自己手动设置 直接用vim打开这个文件编辑 e 如图设置 description字段的内容无所谓 不为空即可 第二
  • c++ 构造函数、析构函数、拷贝构造函数、虚函数、纯虚函数

    今天主要讲拷贝构造函数 给构造函数 析构函数 虚函数 纯虚函数留个坑 拷贝构造函数 注意事项 1 如果类中有全局堆变量 那么就要考虑是否重写拷贝函数 因为默认的拷贝函数只是简单的赋值 会导致多个实例的堆变量共用同一个堆空间 class A
  • JS中的正则表达式

    正则 什么是正则 正则的常用方法 正则的元字符 普通元字符 边界元字符 正则的限定元字符 特殊元字符 重复元字符 正则的标识符 修饰符 正则表达式的捕获 正则的两大特性 什么是正则 正则就是一种规则的的表达式 书写一段规则 用于匹配一段字符
  • react + antdPro 从构建到运行 + 路由 + 组件 + 配置

    不定期更新 好久没用了 这次自己重新熟悉下 有不对的地方或错误 希望大家多指正 快速构建 使用 create react app 快速构建 React 开发环境 cnpm install g create react app create
  • 钉钉微应用开发调试

    1 下载安卓调试包 2 安卓手机点击 设置 开发者选项 USB调试 3 打开钉钉 设置 通用 开发者选项 微应用调试 4 手机连接到电脑 打开chrome chrome inspect 开始调试 如果chrome inspect显示错误页面
  • 超声波模块的使用

    C SR04超声波测距模块可提供约2cm400厘米的非接触式距离感测功能 测距精度可达高到3毫米 模块包括超声波发射器 接收器与控制电路像智能小车的测距以及转向 或是一些项目中 常常会用到 智能小车测距可以及时发现前方的障碍物 使智能小车可
  • 【Unity3D】在Unity中使用Protobuf(proto3)

    有研究表明 一条消息数据 用protobuf序列化后的大小是json的10分之一 xml格式的20分之一 是二进制序列化的10分之一 ProtoBuf的优势还是很明显的 这里简单介绍哈使用 一 下载protobuf https github
  • css移动端页面单位,移动端web开发PX单位问题

    在页面开发过程中 对于设计给的设计稿 一般的PC页面开发的时候 很简单 使用px为单位 给多少px就标注多少 但是到了移动端的web开发 一般标注的单位是dp 这对于css里面px单位就需要转换一下了 这边简单说明一下相关点 几个概念 设备
  • linux启动/重启mongo时报错$OPTIONS (code=exited, status=14)

    遇到过多次这个问题 在此记录一下 网上查了很多 但关于为什么原本好好的 mongo 会报此类错误的原因分析的文章并不多 目前发现 没有正确退出 mongo mongo 配置文件修改错误 修改 mongo 配置文件后重启 等 都有可能导致 m
  • shift用计算机,shift是什么意思_shift键有什么用? - 学无忧

    在我们的电脑键盘上有一个shift上档键 shift是什么意思 shift键有什么用 经常使用电脑的人都会用到这个shift键 这个键跟ctrl和atl键都是很常用的按键 学无忧就来针对shfit来具体讲解 shift是什么意思 shift
  • Mysql 联合索引最左匹配原则

    最左前缀匹配原则 在MySQL建立联合索引时会遵守最左前缀匹配原则 即最左优先 在检索数据时从联合索引的最左边开始匹配 Mysql会一直向右匹配直到遇到范围查询 gt lt between like 就停止匹配了 就比如 a 3 and b
  • 算法学习笔记:labuladong--滑动窗口

    算法技巧的思路非常简单 就是维护一个窗口 不断滑动 然后更新答案 int left 0 right 0 while right lt s size 增大窗口 window add s right right while window nee
  • Python中 sys.argv[]的用法简明解释

    因为是看书自学的python 开始后不久就遇到了这个引入的模块函数 且一直在IDLE上编辑了后运行 试图从结果发现它的用途 然而结果一直都是没结果 也在网上查了许多 但发现这个问题的比较详细的解释只有一个版本 大部分都是转载和复制的 给的都
  • 大数据入门学习

    https www cnblogs com xing901022 p 6195422 html
  • redis之list基本操作

    我们存多个数据用hash的时候它是没有顺序的 我们平时操作 实际上数据很多情况下都是有顺序的 那有没有一种能够用来存储带有顺序的这种数据模型呢 list就专门来干这事儿 一 list类型 数据存储需求 存储多个数据 并对数据进入存储空间的顺
  • 常用的两个免费可以商用的cc0协议图片网站

    什么是CC0协议CC0是CC协议 知识共享 是Creative Commons在中国大陆地区的通用译名 一般简称为CC CC既是该国际组织的名称缩写 也是一种版权授权协议的统称 以外的一种新的版权声明协议 采用该协议即代表作者宣布放弃该创作
  • ROS Noetic版本 rosdep找不到命令 不能使用的解决方法

    使用rosdep指令来安装开源包所需的依赖是很方便的 本文主要介绍ROS Noetic版本中使用rosdep 报错找不到命令 rosdep不能使用的解决方法 rosdep 找不到命令 Command rosdep not found but
  • nginx 中文url rewrite 404

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 原料 nginx with debug或openresty 背景 项目中有用户图片库需求 允许用户自定义文件夹 然后上传图片到该文件夹 当用户自定义的文件夹为中文或者访问u