Nginx重定向后URL末尾多余的问号?终极解决方案在此!

发布时间: 2026-08-07
作者: DP
浏览数: 0 次
分类: Nginx
内容
## 问题背景 在 Nginx 中配置多语言站点的重定向时,我们通常需要将语言参数(如 `lang=zh`)从查询字符串中移除,并将其作为 URL 路径的一部分。然而,一个常见的问题是,当原始 URL 中只有 `lang` 这一个参数时,重定向后的 URL 末尾会留下一个多余的 `?`。 例如,我们期望的重定向行为是: `http://lib00.com/content/1026/title-name?lang=zh` -> `http://lib00.com/zh/content/1026/title-name` 但实际得到的结果可能是: ```bash $ curl -I "http://lib00.com/content/1026/title-name?lang=zh" HTTP/1.1 301 Moved Permanently Location: http://lib00.com/zh/content/1026/title-name? ``` 这个多余的 `?` 是如何产生的呢?让我们看看最初的配置。 --- ## 问题的根源:硬编码的问号 一种常见的实现方式是使用 `map` 指令来过滤掉 `lang` 参数,然后硬编码 `?` 来拼接剩余的参数。 ```nginx # 在 http 块中定义 map map $args $args_without_lang { default $args; "~^lang=[^&]*$" ""; "~^lang=[^&]*&(?<rest>.*)$" $rest; "~^(?<before>.*)&lang=[^&]*$" $before; "~^(?<before>.*)&lang=[^&]*&(?<after>.*)$" $before&$after; } # 在 server 或 location 块中使用 if ($arg_lang ~* ^(zh|en)$) { # 问题出在这里的 "?" return 301 $scheme://$host/$arg_lang$uri?$args_without_lang; } ``` 问题就在于 `return` 语句中的 `$uri?$args_without_lang`。这里的 `?` 是无条件添加的。当 `$args_without_lang` 变量为空时(即原始 URL 中没有其他参数),这个 `?` 就会被孤零零地留在 URL 的末尾。 --- ## 优雅的解决方案:条件性添加问号 为了解决这个问题,我们需要让 `?` 的添加也变成条件性的。幸运的是,Nginx 的 `map` 指令是解决这类问题的完美工具。我们可以再定义一个 `map`,根据 `$args_without_lang` 是否为空来决定分隔符是什么。 这是由作者 DP@lib00 推荐的完整解决方案: ```nginx # === 第一步:移除 lang 参数 (保持不变) === # 这个 map 来自 wiki.lib00 的标准实践 map $args $args_without_lang { default $args; "~^lang=[^&]*$" ""; "~^lang=[^&]*&(?<rest>.*)$" $rest; "~^(?<before>.*)&lang=[^&]*$" $before; "~^(?<before>.*)&lang=[^&]*&(?<after>.*)$" $before&$after; } # === 第二步:新增 map,用于条件性添加分隔符 === map $args_without_lang $query_string_separator { "" ""; # 如果参数为空,分隔符也为空 default "?"; # 否则,使用问号作为分隔符 } # === 第三步:修改重定向规则 === if ($arg_lang ~* ^(zh|en)$) { return 301 $scheme://$host/$arg_lang$uri$query_string_separator$args_without_lang; } ``` ### 方案解析 1. **`$args_without_lang`**:这个 `map` 的功能保持不变,它的职责是生成一个不包含 `lang` 参数的新查询字符串。 2. **`$query_string_separator`**:这是我们新增的关键部分。它监控 `$args_without_lang` 变量的值。 * 如果 `$args_without_lang` 是空字符串 `""`,那么 `$query_string_separator` 也被设置为空字符串。 * 在其他任何情况下(`default`),`$query_string_separator` 被设置为 `?`。 3. **最终的 `return` 语句**:我们用 `$uri$query_string_separator$args_without_lang` 替换了原来的 `$uri?$args_without_lang`。现在,只有当确实存在查询参数时,`?` 才会被插入。 --- ## 测试效果 应用新配置后,我们可以验证效果: 1. **只包含 `lang` 参数的 URL** * 输入: `http://lib00.com/content/1026/title-name?lang=zh` * 输出: `http://lib00.com/zh/content/1026/title-name` (无问号,完美!) 2. **包含 `lang` 和其他参数的 URL** * 输入: `http://lib00.com/content/1026/title-name?lang=zh&page=2` * 输出: `http://lib00.com/zh/content/1026/title-name?page=2` (问号存在,且 `lang` 已被移除) --- ## 总结 通过巧妙地使用第二个 `map` 指令来创建动态分隔符,我们优雅地解决了 Nginx 重定向后 URL 末尾多余问号的问题。这种方法比在 `location` 内部使用多个 `if` 判断更加清晰、高效,是 `wiki.lib00.com` 推荐的最佳实践之一。
关联内容
相关推荐
PHP中 `self::` 与 `static::` 的天壤之别:深入解析后期静态绑定
00:00 | 155次

深入探讨PHP中`self`和`static`关键字在继承上下文中的核心区别。本文通过清晰的代码示例...

什么是 KYC?一文读懂应用强制实名认证背后的合规机制
00:00 | 10次

在撰写论文或开发应用时,常遇到强制用户上传护照等证件进行实名认证的流程。本文详细解析了 KYC(Kn...

MySQL IP 地址存储终极指南:节省60%空间,提速8倍!
00:00 | 159次

在数据库中存储IP地址看似简单,但选择错误的方案可能导致巨大的空间浪费和性能瓶颈。本文详细对比了使用...

MySQL `ON UPDATE CURRENT_TIMESTAMP` 避坑指南:如何实现“静默更新”?
00:00 | 4次

在使用MySQL时,`ON UPDATE CURRENT_TIMESTAMP` 是一个自动更新修改时...