当 slug_generation_method = encoded 时,百分号编码的波斯语标签 URL 出现无限重定向

摘要

当 Discourse 配置为 slug_generation_method = encoded 时,使用百分号编码的波斯语 slug 的标签页面可能会陷入无限 HTTP 重定向循环。

标签存储的 slug 和生成的 URL 看似一致,但请求规范 URL 时,会返回指向完全相同 URL 的 301 Moved Permanently 重定向。

环境

  • Discourse:自托管安装

  • Slug 生成方法:encoded

  • 语言:波斯语(Farsi),使用非 ASCII 字符

  • 受影响的路由:/tag/:tag_slug/:tag_id

复现步骤

  1. 配置 Discourse 使用 slug_generation_method = encoded。

  2. 创建或识别一个具有波斯语名称的标签,例如 نمونه。

  3. 打开该标签的规范 URL,使用其实际的 slug 和 ID。例如:

    /tag/%D9%86%D9%85%D9%88%D9%86%D9%87/123

  4. 检查 HTTP 响应。

实际行为

请求返回 301 Moved Permanently,且 Location 头指向完全相同的 URL。反复跟随重定向会产生无限重定向循环,导致无法直接打开标签页面。

在观察到的环境中,进行了以下比较:

  • 规范的百分号编码波斯语标签 URL 返回了指向自身的 301。

  • 对应的双重百分号编码 URL 返回了 200。

  • 具有拉丁文 slug 的标签在使用其规范 URL 时返回了 200。

预期行为

波斯语标签的规范 URL 应成功返回标签页面,而不应重定向到自身。

如果请求的 slug 不是规范的,Discourse 应仅重定向一次到正确的规范 URL,而不是反复重定向到同一个 URL。

相关代码

在 TagsController#should_redirect_tag? 中,基于 ID 的路由将请求的 slug 直接与存储的 URL slug 进行比较:

params[:tag_slug] != tag.slug_for_url

相比之下,TopicsController#slugs_do_not_match 明确处理了 encoded slug 生成方法:

if SiteSetting.slug_generation_method != "encoded"
  params[:slug] && @topic_view.topic.slug != params[:slug]
else
  params[:slug] && CGI.unescape(@topic_view.topic.slug) != params[:slug]
end

这种差异表明,当 slug_generation_method = encoded 时,标签 slug 的比较可能未正确考虑百分号编码的非 ASCII slug。

可能原因

标签控制器可能在将解码后的路由参数与百分号编码的存储 slug 进行比较,导致即使请求使用的是规范 URL,比较也会失败。由此产生的重定向又指回了同一个 URL。

这是基于观察到的 HTTP 行为和代码比较得出的假设。

建议修复方案

标签 slug 的比较能否像主题 slug 比较一样,一致地处理 slug_generation_method = encoded?

请同时考虑添加覆盖以下场景的请求规格说明(request specs):

  • 具有波斯语或其他非 ASCII 名称的标签的规范 URL。

  • 规范百分号编码标签 URL 的成功渲染。

  • 对真正非规范 slug 的正确重定向。

  • 防止重定向到完全相同的 URL。

影响

用户可能无法直接或在新的浏览器标签页中打开受影响的波斯语标签页面。该问题还可能影响论坛其他位置指向这些页面的链接。

我将不胜感激能确认此行为是否可复现,以及是否应该提供修复方案或回归测试。