英文:
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/<foo>/<bar><anything>
is rewritten to details.php?id=<bar>
, seemingly discarding <foo>
(the 2nd path segment) and ignoring <anything>
. 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
-->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论