在Apache上重定向未生效

huangapple go评论74阅读模式
英文:

Redirect not redirecting on Apache

问题

以下是 .htaccess 文件的内容:

Options All -Indexes
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(([a-z0-9_]+\.)? DOMAIN NAME \.in)$ [NC]
RewriteRule .? https://%1%{REQUEST_URI} [R=301,L]
RewriteRule ^itinerary/([A-Za-z0-9-]+)/([A-Za-z0-9]+) details.php?id=$2 [NC,L]
ErrorDocument 404 /404.php

问题是,当我输入一个不存在的 URL,如 DOMAIN/non-existing 时,错误重定向工作得很好,但当我使用某些重定向到不存在的 URL 时,错误页面无法正确重定向。

样本不存在的链接 是否显示 404.php?
domain/i-donot-exist
domain/i-donot-exist-too.php
domain/itinerary/something/abc
domain/itinerary/something/abc/123
domain/itinerary/something/login.php
domain/itinerary/something/abc/123/1.php
英文:

Below is the content of .htaccess file

Options All -Indexes
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.(([a-z0-9_]+\.)? DOMAIN NAME \.in)$ [NC]
RewriteRule .? https://%1%{REQUEST_URI} [R=301,L]
RewriteRule ^itinerary/([A-Za-z0-9-]+)/([A-Za-z0-9]+) details.php?id=$2 [NC,L]
ErrorDocument 404 /404.php

The problem is when I type a non-existing url as DOMAIN/non-existing the error redirect works perfectly but the problem comes when I use some of the redirect to a non-existing url. The error page does not redirect properly.

Sample non-existing link 404.php shown ?
domain/i-donot-exist YES
domain/i-donot-exist-too.php YES
domain/itinerary/something/abc NO
domain/itinerary/something/abc/123 NO
domain/itinerary/something/login.php NO
domain/itinerary/something/abc/123/1.php NO

答案1

得分: 2

ErrorDocument指令定义了一个Apache错误文档。当Apache确定存在错误时,将提供这个文档。(这不是通过"重定向"提供的,正如您的标题所暗示的那样,而是通过"内部子请求"提供的。)

RewriteRule ^itinerary/([A-Za-z0-9-]+)/([A-Za-z0-9]+) details.php?id=$2 [NC,L]

不显示/404.php响应的4个URL都是由第二个RewriteRule指令(上面)成功写入details.php的。从Apache的角度来看,没有404错误。控制权已交给了details.php,现在由PHP来处理请求,并根据需要提供适当的404响应。(PHP无法看到Apache的ErrorDocument。)

因此,总结一下:

不存在的示例链接 是否显示404.php? 原因
#1 /i-donot-exist 既不映射到文件,也不重写URL
#2 /i-donot-exist-too.php 既不映射到文件,也不重写URL
#3 /itinerary/something/abc URL重写为details.php?id=abc
#4 /itinerary/something/abc/123 URL重写为details.php?id=abc
#5 /itinerary/something/login.php URL重写为details.php?id=login
#6 /itinerary/something/abc/123/1.php URL重写为details.php?id=abc

您的前两个示例根本不匹配上述规则,因此不进行重写,并提供Apache 404 ErrorDocument(前提是它们不映射到物理资源)。

请注意,URL #3、#4和#6都被重写为完全相同的目标!

第二个RewriteRule显然是不正确的(即使不知道应该由它重写的URL)。在正则表达式上没有字符串结束锚点(即$)的情况下,它基本上会重写得太多(如上所示)。任何形式为/itinerary/<foo>/<bar><anything>的URL都会被重写为details.php?id=<bar>,似乎丢弃了<foo>(第二路径段)并忽略了<anything>。在不了解更多关于此处发生的情况的信息的情况下,似乎丢弃第二路径段的事实是奇怪的,可能会导致网站出现重复内容问题。

因此,只需添加字符串结束锚点:

RewriteRule ^itinerary/[A-Za-z0-9-]+/([A-Za-z0-9]+)$ details.php?id=$1 [NC,L]

此时,您的示例中的最后3个URL将无法重写(/itinerary/something/abc/123/itinerary/something/login.php/itinerary/something/abc/123/1.php),因为正则表达式不再匹配,所以将提供Apache 404错误文档。

但是,/itinerary/something/abc(您的第三个示例)仍将被写入details.php(因为它匹配上述正则表达式),因此不会为此URL提供Apache 404错误文档。如果此URL不应该重写为details.php,则需要提供更多信息说明为什么不应该这样做。

因此,在对正则表达式进行上述“微调”之后,我们现在有以下结果:

不存在的示例链接 是否显示404.php? 原因
#1 /i-donot-exist 既不映射到文件,也不重写URL
#2 /i-donot-exist-too.php 既不映射到文件,也不重写URL
#3 /itinerary/something/abc URL重写为details.php?id=abc
#4 /itinerary/something/abc/123 既不映射到文件,也不重写URL
#5 /itinerary/something/login.php 既不映射到文件,也不重写URL
#6 /itinerary/something/abc/123/1.php 既不映射到文件,也不重写URL

关于#3,除非了解更多关于“有效”URL格式的信息,否则无法做更多工作。但我怀疑这应该在您的PHP脚本中处理,而不是在Apache中。

英文:

The ErrorDocument directive defines an Apache error document. A document that will be served when Apache determines there is an error. (This is not served by a "redirect" as your title suggests, but via an "internal subrequest".)

> RewriteRule ^itinerary/([A-Za-z0-9-]+)/([A-Za-z0-9]+) details.php?id=$2 [NC,L]

The 4 URLs that do not show the /404.php response are all written "successfully" to details.php by the 2nd RewriteRule directive (above). From Apache's perspective there is no 404. Control has been handed to details.php and it is now up to PHP to process the request and serve an appropriate 404 response as required. (PHP cannot see what the Apache ErrorDocument is.)

So, in summary:

Sample non-existing link 404.php shown? Reason
#1 /i-donot-exist YES Does not map to file and URL not rewritten
#2 /i-donot-exist-too.php YES Does not map to file and URL not rewritten
#3 /itinerary/something/abc NO URL rewritten to details.php?id=abc
#4 /itinerary/something/abc/123 NO URL rewritten to details.php?id=abc
#5 /itinerary/something/login.php NO URL rewritten to details.php?id=login
#6 /itinerary/something/abc/123/1.php NO URL rewritten to details.php?id=abc

Your first two examples don't match the above rule at all so no rewriting occurs and the Apache 404 ErrorDocument is served (providing they don't map to a physical resource).

Note that URLs #3, #4 and #6 are all rewritten to the very "same" target!

The 2nd RewriteRule is clearly not correct (even without knowing the URLs that should be rewritten by it). Without an end-of-string anchor (ie. $) on the regex, it basically rewrites too much (as shown above). Any URL of the form /itinerary/&lt;foo&gt;/&lt;bar&gt;&lt;anything&gt; is rewritten to details.php?id=&lt;bar&gt;, seemingly discarding &lt;foo&gt; (the 2nd path segment) and ignoring &lt;anything&gt;. Without knowing more information about what is going on here, the fact you are seemingly discarding the 2nd path segment is strange and potentially opens the site up to duplicate content issues.

So, by simply appending an end-of-string anchor:

RewriteRule ^itinerary/[A-Za-z0-9-]+/([A-Za-z0-9]+)$ details.php?id=$1 [NC,L]

Aside: I removed the parentheses around the second path-segment (since you are not making use of this capturing group) and consequently changed the backreference from $2 to $1 in the substitution string.

This will now fail to rewrite the last 3 URLs in your example (/itinerary/something/abc/123, /itinerary/something/login.php and /itinerary/something/abc/123/1.php) since the regex no longer matches, so the Apache 404 error document will be served.

However, /itinerary/something/abc (your 3rd example) will still be written to details.php (since it matches the above regex) so the Apache 404 error document will not be served for this URL. If this URL should not be rewritten to details.php then you need to provide additional information as to why it should not be.

So, after the above tweak to the regex, we now have the following results:

Sample non-existing link 404.php shown? Reason
#1 /i-donot-exist YES Does not map to file and URL not rewritten
#2 /i-donot-exist-too.php YES Does not map to file and URL not rewritten
#3 /itinerary/something/abc NO URL rewritten to details.php?id=abc
#4 /itinerary/something/abc/123 YES Does not map to file and URL not rewritten
#5 /itinerary/something/login.php YES Does not map to file and URL not rewritten
#6 /itinerary/something/abc/123/1.php YES Does not map to file and URL not rewritten

Nothing more can be done regarding #3 without knowing more about the format of "valid" URLs. But I suspect this should be handled in your PHP script, not Apache.

<!--
Another peculiarity with this rule is that you have two capturing subgroups in the RewriteRule pattern, but you only appear to be interested in the second
-->

huangapple
  • 本文由 发表于 2023年7月18日 02:42:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76707283.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定