我有一个只有 3 个页面的小型 PHP 网站。页面内容动态翻译成荷兰语或英语(我从 URL 中获取语言)
\index.php
\page-one.php
\page-two.php
我要实现以下网址的
https://www.example.com/ => https://www.example.com/en/ or nl/ depending browser language
https://www.example.com/en/ => \index.php
https://www.example.com/en/page-one/ => \page-one.php
https://www.example.com/en/page-two/ => \page-two.php
https://www.example.com/nl/ => \index.php
https://www.example.com/nl/page-one/ => \page-one.php
https://www.example.com/nl/page-two/ => \page-two.php
它在我的 PC 上本地工作,使用 WAMP 和以下 htaccess
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !(/$|\.)
RewriteRule (.*) %{REQUEST_URI}/ [R=301,L]
RewriteCond %{HTTP:Accept-Language} ^nl
RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /nl/$1 [L,R]
RewriteRule ^nl/(.*)$ /$1 [L]
RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /en/$1 [L,R]
RewriteRule ^en/(.*)$ /$1 [L]
但是,当我将其发布在共享虚拟主机(在 OVH)上时,页面名称的子文件夹指向索引文件
OK https://www.example.com/ => https://www.example.com/en/ or nl/
OK https://www.example.com/en/ => \index.php
NOK https://www.example.com/en/page-one/ => \index.php
NOK https://www.example.com/en/page-two/ => \index.php
same for the /nl/
页面仅显示如下
https://www.example.com/en/page-one/page-one/ => \page-one.php
https://www.example.com/en/page-two/page-two/ => \page-two.php
还有这些网址的作品,不应该这样
https://www.example.com/en/page-one/page-two/ => \page-two.php
https://www.example.com/en/page-two/page-one/ => \page-one.php
好像跑了两次htaccess的第10行和第14行
我该如何解决这个问题?
这看起来像是与
MultiViews
(mod_negotiation 的一部分)的冲突。这将解释这是如何在本地工作的,以及实时服务器上的行为差异(我怀疑未启用 MultiViews)。 (虽然它似乎没有解释/en/page-one/page-one/
如何在实时服务器上看似“工作”?但是,这将在启用多视图的情况下在本地工作。这同样适用于/en/page-one/page-two/
- 更多内容见下文。)
在您的 mod_rewrite 指令中,您在任何时候都没有附加
.php
扩展名,因此它们本身不可能工作(除非您请求 page-one.php
- 使用 .php
扩展名 - 直接)。所以,看起来你依赖于 MultiViews(它有效地附加了文件扩展名)。
还有这些网址的作品,不应该这样
https://www.example.com/en/page-one/page-two/ => \page-two.php https://www.example.com/en/page-two/page-one/ => \page-one.php
MultiViews 允许这样的东西“工作”。虽然我希望这是相反的方式。 IE。
/en/page-one/page-two/
会服务/page-one.php
,而不是你建议的page-two.php
?
这里发生的是您的 mod_rewrite 规则在内部将对
/en/page-one/page-two/
的请求重写为 /page-one/page-two/
。然后 MultiViews 发起一个内部子请求到 /page-one.php/page-two/
(/page-two/
只是 PATH-INFO)并且 /page-one.php
被提供。
您需要确保禁用了 MultiViews。然后在适当的地方手动附加
.php
扩展名。但是,您没有说明您是如何管理静态资产(JS、CSS、图像等)的?这些是否应该受到相同的重定向/重写?这些语言也是特定的吗?
我会假设你直接链接到静态资产,所以这些不应该受到 URL 重写。
请尝试以下操作:
# Ensure that MultiViews is disabled
Options -MultiViews
DirectoryIndex index.php
RewriteEngine On
# Abort early if request has already been rewritten
RewriteCond %{ENV:REDIRECT_STATUS} .
RewriteRule ^ - [L]
# Abort early if request maps to a file OR directory (except root)
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule . - [L]
# Append trailing slash to non-assets
RewriteCond %{REQUEST_URI} !(/$|\.)
RewriteRule . %{REQUEST_URI}/ [R=301,L]
# Prefix request with language code if omitted (302 - temporary)
# (Defaults to "en" if not "nl" or omitted)
RewriteCond %{HTTP:Accept-Language}@en (?:^|@)(nl|en)
RewriteRule !^(en|nl)/ /%1%{REQUEST_URI} [R=302,L]
# Rewrite to remove language prefix and append ".php" extension if file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^(?:en|nl)/([^/]+)/$ $1.php [L]
# Otherwise, rewrite to remove the language prefix (handles the DirectoryIndex)
RewriteRule ^(?:en|nl)/(.*) $1 [L]
不需要原始规则块中的
RewriteBase
指令(这里也不需要)。
无需检查
THE_REQUEST
,因为我们在请求已经被内部重写时提前中止(通过检查 REDIRECT_STATUS
环境变量 - 在初始请求中为空,并在第一次成功重写后将其设置为 HTTP 状态).
如果在 Apache 2.4 上,那么您可以在最后两条规则(均重写)上使用
END
标志而不是 L
并删除第一个检查 REDIRECT_STATUS
env var 的“提前中止”规则。 END
标志停止所有处理,因此不会发生“循环”。
进一步改进...如果直接请求
index.php
、page-one.php
或 page-two.php
(即任何具有 .php
扩展名的内容),请考虑重定向请求。
基于@MrWhite 他的回应,以下 .htaccess 做了预期的事情
Options -MultiViews
DirectoryIndex index.php
RewriteEngine On
# Append trailing slash to non-assets
RewriteCond %{REQUEST_URI} !(/$|\.)
RewriteRule . %{REQUEST_URI}/ [R=301,L]
RewriteCond %{HTTP:Accept-Language} ^nl
RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /nl/$1 [L,R=302]
RewriteCond %{THE_REQUEST} \ /+(?!(en|nl)/).*
RewriteRule ^(.*)$ /en/$1 [L,R=302]
# Abort early if request has already been rewritten OR maps to a file OR directory
RewriteCond %{ENV:REDIRECT_STATUS} . [OR]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite to remove language prefix and append ".php" extension if file exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^(?:en|nl)/([^/]+)/$ $1.php [L]
# Otherwise, rewrite to remove the language prefix (handles the DirectoryIndex)
RewriteRule ^(?:en|nl)/(.*) $1 [L]