Mod_rewrite 规则在 .htaccess 中无法更改 URL

Mod_rewrite rules not working in .htaccess to change the URL

我正在尝试重写下面的 URL 但是 URL 没有改变,没有错误。

当前URL:

https://example.com/test/news/?c=value1&s=value2&id=9876

预期 URL:

https://example.com/test/news/value1/value2

我的.htaccess

RewriteEngine On
RewriteRule ^test/news/([^/]*)/([^/]*)$ /test/news/?c=&s=&id=1 [L]

but I've seen many articles where a url such as example.com/display_article.php?articleId=my-article can be rewritten as example.com/articles/my-article for example with .htaccess

但是这里的重点(我认为您遗漏了)是 URL 必须已经在您的应用程序内部进行了更改 - 在您所有的内部 link 中。一个常见的误解是 .htaccess 可以单独用于更改 URL 的格式。虽然 .htaccess 是其中的重要部分,但它只是其中的 部分

是的,您可以在 .htaccess 中实施重定向以从旧的重定向到新的 URL - 这对于保留 SEO 至关重要(见下文),但这对您的网站来说并不重要应用程序工作。如果您不先更改内部 link 中的 URL,则:

  1. “旧”URL 仍然暴露在 HTML 来源中。当用户悬停或复制 link 时,他们看到并复制了“旧”URL.

  2. 每次用户单击您的内部 link 之一时,他们都会从外部重定向到“新”URL。这对你的用户来说很慢,对 SEO 不利(你永远不应该 link 到重定向的 URL)并且对你的服务器不利,因为它可能会使访问你服务器的请求数量加倍(好吧, 301 缓存在本地)。

引用 @IMSoP's answer to this reference question 的主题:

Rewrite rules don't make ugly URLs pretty, they make pretty URLs ugly


因此,一旦您将内部 links 更改为“新”(预期)格式,例如。 /test/news/value1/value2(或者应该是/test/news/value1/value2/id甚至/test/news/id/value1/value2?见下文),那么你可以做如下...

RewriteRule ^test/news/([^/]*)/([^/]*)$ /test/news/?c=&s=&id=1 [L]

这在内部将请求从 /test/news/<value1>/<value2> 重写为 /test/news/?c=<value1>&s=<value2>&id=1。但是,这有几个问题:

  1. /test/news/ 本身不是有效端点。这需要进一步重写。也许您正在提供 DirectoryIndex 文档(例如 index.php)?这对您来说可能看起来是无缝的,但这需要额外的内部子请求并使规则依赖于配置的其他元素。您应该直接重写到处理请求的文件。例如。 /test/news/index.php?c=<value1>&s=<value2>&id=1(记住,这对用户是完全隐藏的)。

  2. 您正在对 id=1 参数进行硬编码?每个 URL 都应该有相同的 id 吗?还是应该在“新”URL 中传递(这是我所期望的)? id 代表什么?如果这对 URL 的路由至关重要,那么 id 应该出现在 URL-path 的前面,以防 URL 在 copy/pasted/shared 时被意外截断。

    如果需要 id,则需要在“新”URL 中传递。我们只有“新”URL 来路由请求,因此无法隐藏信息。

因此,如果“新”URL 现在是 /test/news/<id>/<value1>/<value2>,那么重写就需要像这样:

# Rewrite new URLs to old/actual URL
# "/test/news/<id>/<value1>/<value2>" to "/test/news/?c=<value1>&s=<value2>&id=<id>"
RewriteRule ^test/news/(\d+)/([^/]+)/([^/]+)$ /test/news/?c=&s=&id= [L]

然后(可选*1)您可以实施外部重定向以保留 SEO。这是针对已将无法更新的“旧”URLs 或第三方入站 links 编入索引的搜索引擎 - 这些需要更正以通知搜索引擎更改并让用户使用“新”规范 URL 跟随 out-of-date 入站 link.

(*1 如果您要更改现有 URL,则它不是“可选的”,但对于您的应用程序正在运行。)

此“重定向” 上述重写之前

# Redirect old URLs to the new "canonical" URL
# "/test/news/?c=<value1>&s=<value2>&id=<id>" to "/test/news/<id>/<value1>/<value2>"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^c=([^&]+)&s=([^&]+)&id=(\d+)
RewriteRule ^test/news/$ /[=12=]%3/%1/%2 [QSD,R=301,L]

[=34=] 反向引用包含来自 RewriteRule 模式 的完整匹配,即。 test/news/ 在这种情况下 - 这只是节省了重复。

%1%2%3 反向引用包含从前面的 条件 捕获的值。 IE。 csid URL 参数的值分别为

请注意,URL 参数/路径段不应像您的原始指令(即 ([^/]*))中那样是 可选的 。如果它们是可选的并且被省略,那么结果 URL 就会变得不明确。例如。如果省略 <value1><value2> 变为 <value1>

请注意,URL 参数必须按照规定的顺序排列。如果您的“旧”URL 与这些参数的顺序不同(或者甚至与其他参数混合),那么这可能会导致额外的复杂性。 (在 server-side 脚本中执行此重定向可能比 .htaccess 更容易。)

检查 REDIRECT_STATUS 环境变量的第一个条件确保我们只重定向直接请求,而不是通过以后的重写重写请求(否则会导致重定向循环)。 Apache 2.4 的另一种选择是在 RewriteRule 上使用 END 标志。

QSD 标志 (Apache 2.4) 从请求中丢弃原始查询字符串。

您应该首先使用 302(临时)重定向进行测试以避免潜在的缓存问题,并且只更改为 301(临时)manent) 重定向,一旦你测试了一切都按预期工作。 301 由浏览器持久缓存,因此会使测试出现问题。


总结

您完整的 .htaccess 文件应如下所示:

Options -MultiViews +FollowSymLinks

# If relying on the DirectoryIndex to handle the request
DirectoryIndex index.php

RewriteEngine On

# Redirect old URLs to the new "canonical" URL
# "/test/news/?c=<value1>&s=<value2>&id=<id>" to "/test/news/<id>/<value1>/<value2>"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^c=([^&]+)&s=([^&]+)&id=(\d+)
RewriteRule ^test/news/$ /[=13=]%3/%1/%2 [QSD,R=301,L]

# Rewrite new URLs to old/actual URL
# "/test/news/<id>/<value1>/<value2>" to "/test/news/?c=<value1>&s=<value2>&id=<id>"
RewriteRule ^test/news/(\d+)/([^/]+)/([^/]+)$ /test/news/?c=&s=&id= [L]