百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

nginx解码特殊字符引发400问题处理案例分享

off999 2025-02-15 16:54 18 浏览 0 评论

问题背景和现象

公司任务管理使用的是开源的redmine,以前是单机部署(bitnami_redmine),后来由于项目数量、人员数量和任务数量的增加,卡顿问题比较明显,于是改造为基于k8s的分布式集群部署(nginx+puma)。

改造后有个现象是,wiki标题中,如果包含引号特殊字符,在打开页面时,redmine后台将返回400。

wiki标题如下:

400显示效果如下:

处理办法

方案1:替换数据库中的引号,替换为空

  • 首先查出wiki标题,使用update进行修改。

这里仅为试验,没有使用MySQL的replace函数进行全局替换

  • 之后去掉引用页wiki_content的引号
  • 通过浏览器测试访问正常

小结:

此方案虽然可以通过update … replace … 进行全部替换wikis title字段,但由于redmine wiki页面可以在项目内进行引用(issue和其他wiki均可引用),引用时内容较多,无法区分引号是否需要被替换,所以无法对wiki_contents以及journal_details表进行全局替换

方案2:找到参数丢失的根源

思路大致是先通过对比缩小范围,找到400是哪个节点返回的,之后再进一步分析具体原因。

  • 1.首先看下nginx端是否正常接收到了参数,中文及特殊字符会进行urlencode,我们直接用转码后的结果在kibana中进行搜索
url.original : *%E4%B8%BB%E6%8E%A7%E6%9C%BA%E6%8A%A5%E9%94%99*

可以看到参数到达nginx端时,还是对的,这很有可能是nginx再向后端svc传递时丢失了参数导致。

我们可以通过curl跳过nginx,直接测试svc地址能否正常返回。

  • 2.先复制可以响应200的请求
  • 3.在服务器上跳过nginx直接访问svc地址,url中增加引号字符(%22),同时数据响应状态码-w %{http_code}

从上面的测试可以看出,跳过nginx转发,可以拿到正常的响应结果。

注:简单说明下,此前置nginx由于各类转发问题(比如cdn回源、oss图片转发等)没有使用nginx-ingress-controller暴露k8s集群中的服务,使用的是集群外置nginx。

  • 4.返回400的请求,在rails日志中并没有看到处理过程
  • 5.rails使用的是puma发布,查看puma日志,可以看到转换异常的错误

到这里基本可以定位到问题节点,处于nginx—>puma时,发送的http请求不被puma认可。

而curl发出的请求,是能被puma认可的。

那么区别到底在哪里?nginx发出的请求为何有差异?

查看nginx error日志没有线索,问题到这里已经陷入了困境,难道只能抓包分析下?

  • 6.服务器上使用tcpdump抓包,期间触发两次请求,一次curl svc地址(响应200的),一次curl nginx地址(响应400的)tcpdump -i cnio host 10.10.2.187 -w /tmp/1.pcap
  • 7.从服务器传到本地用wireshark分析

从上面的对比可知,nginx收到浏览器发送的请求后,发现有urlencode后的字符%22,会进行解码后将引号传递到后端。

为何其他中文字符没有解码,唯独解码引号这一个字符?要弄清楚这个问题,需要结合nginx源码看一下。

  • 8.如何避免这个问题?

正常情况下,uri的转义操作在浏览器端已经完成,所以nginx侧的$request_uri肯定是encode后的正确状态,这一点在文章开头中Kibana的access日志中可以看到。我们可以利用这个参数,对location进行特殊处理

修改前的配置如下

location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Real-IP  $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_pass http://10.89.0.8/$1;
}

修改后的配置如下

location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Real-IP  $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       if ($request_uri ~* ^/(.*)$) {
          proxy_pass http://10.89.0.8/$1;
       }
}

写在后面

nginx内置变量很多,配合location rewrite正则可以满足多种转发场景。

有其他解决思路的朋友可以留言哟~。

相关推荐

安装python语言,运行你的第一行代码

#01安装Python访问Python官方(https://www.python.org/),下载并安装最新版本的Python。确保安装过程中勾选“Addpython.exetoPAT...

Python推导式家族深度解析:字典/集合/生成器的艺术

一、为什么需要其他推导式?当你在处理数据时:o需要快速去重→集合推导式o要建立键值映射→字典推导式o处理海量数据→生成器表达式这些场景是列表推导式无法完美解决的,就像工具箱需要不同工...

别再用循环创建字典了!Python推导式让你的代码起飞

当同事还在用for循环吭哧吭哧创建字典时,我早已用推导式完成3个需求了!这个被90%新手忽视的语法,今天让你彻底掌握字典推导式的4大高阶玩法,文末彩蛋教你用1行代码搞定复杂数据转换!基础语法拆解#传...

什么是Python中的生成器推导式?(python生成器的好处)

编程派微信号:codingpy本文作者为NedBatchelder,是一名资深Python工程师,目前就职于在线教育网站Edx。文中蓝色下划线部分可“阅读原文”后点击。Python中有一种紧凑的语法...

Python 列表转换为字符串:实用指南

为什么在Python中将列表转换为字符串?Python列表非常灵活,但它们并非在所有地方都适用。有时你需要以人类可读的格式呈现数据——比如在UI中显示标签或将项目保存到CSV文件。可能还...

生成器表达式和列表推导式(生成器表达式的计算结果)

迭代器的输出有两个很常见的使用方式,1)对每一个元素执行操作,2)选择一个符合条件的元素子集。比如,给定一个字符串列表,你可能想去掉每个字符串尾部的空白字符,或是选出所有包含给定子串的字符串。列表...

python学习——038python中for循环VS列表推导式

在Python中,for循环和列表推导式(ListComprehension)都可以用于创建和处理列表,但它们的语法、性能和适用场景有所不同。以下是两者的详细对比:1.语法结构for循环使用...

python中列表推导式怎么用?(列表 python)

这个问题,我们不妨用近期很火的ChatGPT来试试,来看看人工智能是如何解答的?在Python中,列表解析是一种简洁的方法,用于生成列表。它是一种快速,简洁的方法,可以在一行代码中生成列表,而不需...

Python列表推导式:让你的代码优雅如诗!

每次写for循环都要三四行代码?处理数据时总被嵌套结构绕晕?学会列表推导式,一行代码就能让代码简洁十倍!今天带你解锁这个Python程序员装(偷)逼(懒)神器!一、为什么你需要列表推导式?代码...

python学习——038如何将for循环改写成列表推导式

在Python里,列表推导式是一种能够简洁生成列表的表达式,可用于替换普通的for循环。下面是列表推导式的基本语法和常见应用场景。基本语法result=[]foriteminite...

太牛了!Python 列表推导式,超级总结!这分析总结也太到位了!

Python列表推导式,超级总结!一、基本概念列表推导式是Python中创建列表的一种简洁语法,它允许你在一行代码内生成列表,替代传统的for循环方式。其核心思想是**"对可迭代对...

25-2-Python网络编程-TCP 编程示例

2-TCP编程示例应用程序通常通过“套接字”(socket)向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通信。Python语言提供了两种访问网络服务的功能。其中低级别的网络服...

python编程的基础与进阶(周兴富)(python编程基础视频)

前不久我发文:《懂了,if__name=='__main__'》。想不到的是,这个被朋友称之为“读晕了”的文章,其收藏量数百,有效阅读量竟然过万。所谓“有效阅读量”,就是读到尾部才退...

Python 闭包:深入理解函数式编程的核心概念

一、简介在Python编程领域,闭包(Closure)是一个既基础又强大的概念,它不仅是装饰器、回调函数等高级特性的实现基础,更是函数式编程思想的重要体现。理解闭包的工作原理,能够帮助开发者编写出...

Python小白逆袭!7天吃透PyQt6,独立开发超酷桌面应用

PythonGUI编程:PyQt6从入门到实战的全面指南在Python的庞大生态系统中,PyQt6作为一款强大的GUI(GraphicalUserInterface,图形用户界面)编程框架,为开...

取消回复欢迎 发表评论: