IIS反向代理Spree Commerce图片请求400错误解决咨询
问题背景
使用IIS将请求反向代理至同域名下端口4000的Spree Commerce后端服务器,当前代理重写规则配置如下:
<rule name="Spree backend reverse proxy" stopProcessing="true"> <match ignoreCase="true" url=".*" /> <conditions logicalGrouping="MatchAny" trackAllCaptures="true"> <add input="{REQUEST_URI}" pattern="^/api/v2/storefront(/.*)?" /> <add input="{REQUEST_URI}" pattern="^/rails(/.*)?" /> </conditions> <action type="Rewrite" url="http://{HTTP_HOST}:4000/{R:0}" redirectType="Found" appendQueryString="true" /> </rule>
多数请求可正常代理,但部分图片请求经Spree后端重定向后,返回「Bad Request - Invalid URL」400错误:
- 初始请求路径:
/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBIZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--eaf0cd5b8918b5b97f261c07fe6de1d8434cea0a/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFMQUVta0MwQWM9IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--af08f06f4ce9755789c19150c3998b9022e0f51d/DALL%C2%B7E%202023-12-06%2014.35.19%20-%20Create%20an%20image%20that%20features%20a%20wide%20banner%20with%20a%20background%20pattern%20of%20black%20carbon%20fiber.%20Add%20dynamic%20splashes%20of%20mud%20across%20the%20banner,%20as%20if%20mud%20.png
- 重定向后报错路径:
/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhiWGgyZG5jMmNUSTFablozWm5ab2RuVTJjelJuYXpNemJYWnVjd1k2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpQXQwQmFXNXNhVzVsT3lCbWFXeGxibUZ0WlQwaVJFRk1UQ1V6UmtVZ01qQXlNeTB4TWkwd05pQXhOQzR6TlM0eE9TQXRJRU55WldGMFpTQmhiaUJwYldGblpTQjBhR0YwSUdabFlYUjFjbVZ6SUdFZ2QybGtaU0JpWVc1dVpYSWdkMmwwYUNCaElHSmhZMnRuY205MWJtUWdjR0YwZEdWeWJpQnZaaUJpYkdGamF5QmpZWEppYjI0Z1ptbGlaWEl1SUVGa1pDQmtlVzVoYldsaklITndiR0Z6YUdWeklHOW1JRzExWkNCaFkzSnZjM01nZEdobElHSmhibTVsY2lVeVF5QmhjeUJwWmlCdGRXUWdMbkJ1WnlJN0lHWnBiR1Z1WVcxbEtqMVZWRVl0T0NjblJFRk1UQ1ZETWlWQ04wVWxNakF5TURJekxURXlMVEEySlRJd01UUXVNelV1TVRrbE1qQXRKVEl3UTNKbFlYUmxKVEl3WVc0bE1qQnBiV0ZuWlNVeU1IUm9ZWFFsTWpCbVpXRjBkWEpsY3lVeU1HRWxNakIzYVdSbEpUSXdZbUZ1Ym1WeUpUSXdkMmwwYUNVeU1HRWxNakJpWVdOclozSnZkVzVrSlRJd2NHRjBkR1Z5YmlVeU1HOW1KVEl3WW14aFkyc2xNakJqWVhKaWIyNGxNakJtYVdKbGNpNGxNakJCWkdRbE1qQmtlVzVoYldsakpUSXdjM0JzWVhOb1pYTWxNakJ2WmlVeU1HMTFaQ1V5TUdGamNtOXpjeVV5TUhSb1pTVXlNR0poYm01bGNpVXlReVV5TUdGekpUSXdhV1lsTWpCdGRXUWxNakF1Y0c1bkJqc0dWRG9SWTI5dWRHVnVkRjkwZVhCbFNTSU9hVzFoWjJVdmNHNW5CanNHVkRvUmMyVnlkbWxqWlY5dVlXMWxPZ3BzYjJOaGJBPT0iLCJleHAiOm51bGwsInB1ciI6ImJsb2Jfa2V5In19--8165500c1e1645f6defeeaa959ef7fa6ac43e659/DALL·E%202023-12-06%2014.35.19%20-%20Create%20an%20image%20that%20features%20a%20wide%20banner%20with%20a%20background%20pattern%20of%20black%20carbon%20fiber.%20Add%20dynamic%20splashes%20of%20mud%20across%20the%20banner,%20as%20if%20mud%20.png
已确认请求未到达IIS,HTTPERR日志显示为URL问题。取消ARR服务器代理设置中的「Reverse rewrite host in response headers」可解决问题,但会暴露无SSL证书的Spree后端,不符合安全要求。
问题原因
- 重定向Location头未被正确重写:Spree后端返回重定向时,会在Location头中包含自身的4000端口。ARR的「Reverse rewrite host in response headers」仅处理Host头,未覆盖Location头,导致客户端拿到带4000端口的URL,请求时因IIS未监听该端口(或无对应SSL证书)触发400错误。
- URL长度超出IIS默认限制:Active Storage生成的URL较长,超过IIS默认的
maxUrlLength限制,也会引发400错误。 - URL中的特殊字符(如
·)已正确编码为%C2%B7,符合规范,不是错误原因。
安全解决方案
1. 添加ARR出站规则重写Location头
在IIS站点的web.config中添加出站规则,将Spree返回的带4000端口的Location头替换为前端HTTPS域名:
<system.webServer> <rewrite> <outboundRules rewriteBeforeCache="true"> <rule name="Rewrite Spree Redirect Location" stopProcessing="true"> <match serverVariable="RESPONSE_Location" pattern="^http://(.*):4000/(.*)" /> <action type="Rewrite" value="https://{R:1}/{R:2}" /> </rule> </outboundRules> </rewrite> </system.webServer>
该规则会拦截响应中的Location头,将http://domain.com:4000/path重写为https://domain.com/path,确保客户端请求走IIS代理,不会暴露后端。
2. 调整IIS URL长度限制
修改web.config,增大URL和查询字符串的长度限制,适配Active Storage的长URL:
<system.web> <httpRuntime maxUrlLength="10999" maxQueryString="2097151" /> </system.web> <system.webServer> <security> <requestFiltering> <requestLimits maxUrl="10999" maxQueryString="2097151" /> </requestFiltering> </security> </system.webServer>
3. 配置Spree使用正确的前端域名
在Spree的环境配置文件(如config/environments/production.rb)中设置正确的URL选项,让Spree直接生成前端域名的重定向URL,减少后续重写工作:
# 设置全局URL选项 config.action_controller.default_url_options = { host: 'your-domain.com', protocol: 'https' } # 设置Active Storage URL选项 config.active_storage.url_options = { host: 'your-domain.com', protocol: 'https' }
内容的提问来源于stack exchange,提问作者Hrithvik Gadhiya
相关产品推荐
相关产品推荐

