最近在工作中遇到了一次神奇的http400事情,记录一下。

起因

由于项目架构要求,需要开发一个proxy服务,用来统一转发外部的请求给后端服务。

在本地测试时,后端服务爆出400错误,而在测试环境没有问题。

排查

由于本地之前接连出现504,502等错误,便继续从nginx的配置开始排查,但是没有发现什么问题。

把nginx的日志级别,调整为info,发现问题所在,无效的header: content-length,原因是值为空字符串。

可是为什么测试环境没有这个问题呢?

在proxy发送请求之前,打印出header,发现两个环境是一致的,content-length都是空字符串。

难道测试环境的nginx可以接收值为空字符串的content-length?

试一试使用postman直接在header给content-length赋值为为空字符串,测试环境和本地都会直接报错。

那是测试环境的后端服务收到的header里,content-length是正确的?

在proxy的nginx的access-log 中打印$content-length,观察得出如果传0则显示0,不传则显示-,传空字符串则报错,无log。

然后打印后端服务access.log,观察content-length头,发现在测试环境中,虽然proxy也发送了值为空字符串的content-length,但是service收到的值和不传是一样的,猜测是某个地方处理了content-length,删除了这个头。

对比一下代码版本,代码使用的三方包版本一致,框架一致,nginx版本不一致,配置一致,fpm版本一致,本地环境换了nginx还是有这个问题。

猜测是系统网络包实现不同。

总结

本地解决方案,在fastcgi.conf 中添加if_not_empty$content-length,作用是只有不为空值的时候,才传递content-length。

1
fastcgi_param  CONTENT_LENGTH     $content_length if_not_empty;

要做的事

这个问题很神奇,实际上最后也没什么找出到底是什么原因引起的,之后有时间的话,可能会继续深入了解一下,不过这也说明了维持环境一致是非常重要的事情(某种意义上来说,维持环境不一致也很重要),这也是很多时候我非常反感人们说mac跟linux是一样的原因。