提问人:Chris Aelbrecht 提问时间:3/30/2023 更新时间:4/1/2023 访问量:81
使用虚拟语言文件夹重写 htaccess URL 将页面名称加倍为 URL 中的文件夹
htaccess url rewrite with virtual language folders doubles page name as folder in URL
问:
我有一个有 3 页的小型 PHP 网站。页面内容动态翻译成荷兰语或英语(我从URL中获取语言)
\index.php
\page-one.php
\page-two.php
我想实现以下 URL
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
但也有这些URL的作品,这不应该是这样的
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 行。
我该如何解决这个问题?
答:
这看起来像是与(mod_negotiation的一部分)发生冲突。这将解释它如何在本地工作,以及实时服务器上的行为差异(我怀疑 MultiView 未启用)。(虽然它似乎没有解释在实时服务器上似乎是如何“工作”的?但是,这将在启用 MultiView 的情况下在本地工作。这同样适用于 - 更多内容见下文。MultiViews
/en/page-one/page-one/
/en/page-one/page-two/
在您的 mod_rewrite 指令中,您不会在任何时候附加扩展,因此它们本身不可能工作(除非您直接请求 - 使用扩展)。因此,看起来您正在依赖 MultiView(它有效地附加了文件扩展名)。.php
page-one.php
.php
但也有这些URL的作品,这不应该是这样的
https://www.example.com/en/page-one/page-two/ => \page-two.php https://www.example.com/en/page-two/page-one/ => \page-one.php
正是 MultiView 允许这样的东西“工作”。虽然我希望情况正好相反。即。 会服务,而不是像你建议的那样?/en/page-one/page-two/
/page-one.php
page-two.php
这里发生的情况是,您的 mod_rewrite 规则在内部重写了对 的请求。然后,MultiView 向 ( 简称为 PATH-INFO) 发起一个内部子请求,并为其提供服务。/en/page-one/page-two/
/page-one/page-two/
/page-one.php/page-two/
/page-two/
/page-one.php
您需要确保禁用 MultiView。然后在适当的情况下手动追加扩展。但是,您还没有说明您是如何管理静态资产(JS、CSS、图像等)的?这些是否应该受到相同的重定向/重写?这些语言也是特定的吗?.php
我假设您直接链接到静态资产,因此这些资产不应受到 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
无需检查,因为当请求已经在内部重写时,我们会提前中止(通过检查环境变量 - 该变量在初始请求中为空,并在第一次成功重写后设置为 HTTP 状态)。THE_REQUEST
REDIRECT_STATUS
如果在 Apache 2.4 上,那么您可以在最后两条规则(均重写)上使用标志而不是 ,并删除第一个检查环境变量的“提前中止”规则。该标志停止所有处理,因此不会发生“循环”。END
L
REDIRECT_STATUS
END
进一步的改进...如果直接请求 ,或(即任何带有扩展名的内容),请考虑重定向请求。index.php
page-one.php
page-two.php
.php
评论
RewriteCond
.php
index.php
en
^
accept-language: fr-CH,fr;q=0.9,en-GB;q=0.8,en;q=0.7,de-CH;q=0.6,de;q=0.5
nl
en
根据@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]
评论
https://www.example.com/en/page-one/page-one/ => \page-one.php