使用 .htaccess 将多个域动态指向单个域的子文件夹
Dynamically pointing multiple domains to a single domain's subfolder with .htaccess
在这个例子中,我管理 domain.com
。在其中,我在 php 中有一个商店模板,可以动态加载所选商店:
domain.com/store1
domain.com/store2
domain.com/store3
下面的 .htaccess
正在生成漂亮的网址。后面真正加载的是:
domain.com/store/index.php?store=store1
domain.com/store/index.php?store=store2
domain.com/store/index.php?store=store3
每个商店都有内部链接,例如:
domain.com/store1/catalogue
domain.com/store1/catalogue/instruments
domain.com/store1/catalogue/instruments/guitars
domain.com/store1/electric-guitar/1337
它完全符合预期。这些是在 domain.com
中工作的 .htaccess
规则。每个 RewriteRule
中的 store
查询字符串(store1
、store2
、store3
)确定正在加载哪个商店:
# permalinks
RewriteEngine on
# errors
ErrorDocument 404 /error.php?error=404
# ignore existing directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
#################################################
# store
RewriteRule ^([0-9a-zA-Z-]{2,50})/?$ store/index.php?store= [QSA,L]
# store: catalogue
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/?$ store/catalogue.php?store= [QSA,L]
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/([0-9a-zA-Z-]{1,75})/?$ store/catalogue.php?store=&cat= [QSA,L]
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/([0-9a-zA-Z-]{1,75})/([0-9a-zA-Z-]{1,75})/?$ store/catalogue.php?store=&cat=&subcat= [QSA,L]
# store: products
RewriteRule ^([0-9a-zA-Z-]{2,50})/([0-9a-zA-Z-]{1,150})/([0-9]+)$ store/product.php?store=&slug=&id_product= [QSA,L]
现在,棘手的部分来了。一些客户希望使用他们自己的域,以便 store1.com/*
加载与 domain.com/store1/*
相同的内容(不使用重定向)。
示例:
store1.com
=> domain.com/store1/
store1.com/catalogue
=> domain.com/store1/catalogue
store1.com/catalogue/instruments
=> domain.com/store1/catalogue/instruments
store1.com/catalogue/instruments/guitars
=> domain.com/store1/catalogue/instruments/guitars
store1.com/electric-guitar/1337
=> domain.com/store1/electric-guitar/1337
你懂的。
所有域(domain.com
、store1.com
、store2.com
、store3.com
)都配置在相同的 Apache Web 服务器环境中(使用虚拟主机)。
问题是:这可以在每个商店的域根路径中的 .htaccess
文件中动态实现,还是在虚拟主机 .conf
文件中实现?如果是,怎么办?
确保所有自定义“商店”域(例如 store1.com
、store2.com
等)在 domain.com
vHost 中定义为 ServerAlias
等解析到与 domain.com
.
相同的位置
然后您可以重写自定义商店域的请求,在 URL 路径前加上 store-id(域名),然后使用您现有的规则不变。
例如,store1.com/catalogue/instruments
的请求在内部被重写 为/store1/catalogue/instruments
,然后由现有规则照常处理。
以下指令应该 在 现有 # store
规则之前:
# Internally rewrite the request when a custom domain is used
# - the URL-path is prefixed with the domain name
RewriteCond %{HTTP_HOST} !^(www\.)?domain\. [NC]
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 !^/([^/]+)/.*@
RewriteRule (.*) %1/
基本上就是这样,尽管您也可以决定实施规范重定向 - 见下文。
以上指令的解释:
第一个 条件 简单地排除了对主要 domain.com
的请求(它应该已经有相关的 store-id 作为 URL 路径的前缀)。
%{REQUEST_URI} !\.\w{2,4}$
- 第二个条件避免重写对静态资源(图片、JS、CSS等)的请求。具体来说,它排除了任何以 - 看起来像 - 文件扩展名结尾的请求。
%{HTTP_HOST} ^([^.]+)
- 第三个 条件 在 TLD 之前捕获请求的域名,之后可以使用 %1
反向引用访问并用于作为 URL 路径的前缀。这假设没有 www 子域(如评论中所述)。例如。给定 store1.com
或 store2.co.uk
的请求,分别捕获 store1
或 store2
。
%{REQUEST_URI}@%1 !^/([^/]+)/.*@
- 第四个 condition 检查 URL-path 是否已经带有域名前缀(上面捕获的) ).这主要是为了保证重写的请求不会被再次重写,造成重写循环。这是使用内部反向引用 (</code>) 实现的,该反向引用将 URL 路径中的第一个路径段与先前捕获的域名 (<code>%1
).
进行比较
(.*) %1/
- 最后,请求在内部重写,在请求的 URL-path.
前加上域名前缀
忽略现有文件(可能不需要)
# ignore existing directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
您没有说明如何引用您的静态资源(图像、JS、CSS 等),但您可能需要修改此规则以匹配对现有文件的请求。 (尽管我在上面的规则中添加的条件可能已经足以排除这些请求。)例如:
# ignore existing directories or files
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
规范重定向(可选)
您还应该考虑 重定向 形式 store1.com/store1/catalogue/instruments
的任何直接请求返回规范 URL store1.com/catalogue/instruments
- 如果这些 URL 永远是 exposed/discovered。 store1.com/store2/catalogue/instruments
形式的请求自然会导致 404,所以这不是问题。
例如,以下内容将紧跟在您现有规则中的 ErrorDocument
指令之后:
# Redirect to remove the "/store1" URL-prefix when the "store1.com" domain is requested.
# - Only applies to direct requests.
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTP_HOST} !^domain\. [NC]
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 ^/([^/]+)/.*@
RewriteRule ^(?:[^/]+)(/.*) [R=301,L]
首先使用 302(临时)重定向进行测试,以避免潜在的缓存问题。
这基本上是上述重写的反向,但仅适用于直接请求。针对 REDIRECT_STATUS
env var 的检查可确保仅处理来自客户端的直接请求,而不处理后来重写的重写请求。
总结
有了两个规则块...
# permalinks
RewriteEngine on
# errors
ErrorDocument 404 /error.php?error=404
# Redirect to remove the "/store1" URL-prefix when the "store1.com" domain is requested.
# - Only applies to direct requests.
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTP_HOST} !^domain\. [NC]
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 ^/([^/]+)/.*@
RewriteRule ^(?:[^/]+)(/.*) [R=301,L]
# ignore existing directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Internally rewrite the request when a custom domain is used
# - the URL-path is prefixed with the domain name
RewriteCond %{HTTP_HOST} !^(www\.)?domain\. [NC]
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 !^/([^/]+)/.*@
RewriteRule (.*) %1/
#################################################
# store
:
: existing directives follow
:
在这个例子中,我管理 domain.com
。在其中,我在 php 中有一个商店模板,可以动态加载所选商店:
domain.com/store1
domain.com/store2
domain.com/store3
下面的 .htaccess
正在生成漂亮的网址。后面真正加载的是:
domain.com/store/index.php?store=store1
domain.com/store/index.php?store=store2
domain.com/store/index.php?store=store3
每个商店都有内部链接,例如:
domain.com/store1/catalogue
domain.com/store1/catalogue/instruments
domain.com/store1/catalogue/instruments/guitars
domain.com/store1/electric-guitar/1337
它完全符合预期。这些是在 domain.com
中工作的 .htaccess
规则。每个 RewriteRule
中的 store
查询字符串(store1
、store2
、store3
)确定正在加载哪个商店:
# permalinks
RewriteEngine on
# errors
ErrorDocument 404 /error.php?error=404
# ignore existing directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule .* - [L]
#################################################
# store
RewriteRule ^([0-9a-zA-Z-]{2,50})/?$ store/index.php?store= [QSA,L]
# store: catalogue
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/?$ store/catalogue.php?store= [QSA,L]
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/([0-9a-zA-Z-]{1,75})/?$ store/catalogue.php?store=&cat= [QSA,L]
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/([0-9a-zA-Z-]{1,75})/([0-9a-zA-Z-]{1,75})/?$ store/catalogue.php?store=&cat=&subcat= [QSA,L]
# store: products
RewriteRule ^([0-9a-zA-Z-]{2,50})/([0-9a-zA-Z-]{1,150})/([0-9]+)$ store/product.php?store=&slug=&id_product= [QSA,L]
现在,棘手的部分来了。一些客户希望使用他们自己的域,以便 store1.com/*
加载与 domain.com/store1/*
相同的内容(不使用重定向)。
示例:
store1.com
=>domain.com/store1/
store1.com/catalogue
=>domain.com/store1/catalogue
store1.com/catalogue/instruments
=>domain.com/store1/catalogue/instruments
store1.com/catalogue/instruments/guitars
=>domain.com/store1/catalogue/instruments/guitars
store1.com/electric-guitar/1337
=>domain.com/store1/electric-guitar/1337
你懂的。
所有域(domain.com
、store1.com
、store2.com
、store3.com
)都配置在相同的 Apache Web 服务器环境中(使用虚拟主机)。
问题是:这可以在每个商店的域根路径中的 .htaccess
文件中动态实现,还是在虚拟主机 .conf
文件中实现?如果是,怎么办?
确保所有自定义“商店”域(例如 store1.com
、store2.com
等)在 domain.com
vHost 中定义为 ServerAlias
等解析到与 domain.com
.
然后您可以重写自定义商店域的请求,在 URL 路径前加上 store-id(域名),然后使用您现有的规则不变。
例如,store1.com/catalogue/instruments
的请求在内部被重写 为/store1/catalogue/instruments
,然后由现有规则照常处理。
以下指令应该 在 现有 # store
规则之前:
# Internally rewrite the request when a custom domain is used
# - the URL-path is prefixed with the domain name
RewriteCond %{HTTP_HOST} !^(www\.)?domain\. [NC]
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 !^/([^/]+)/.*@
RewriteRule (.*) %1/
基本上就是这样,尽管您也可以决定实施规范重定向 - 见下文。
以上指令的解释:
第一个 条件 简单地排除了对主要
domain.com
的请求(它应该已经有相关的 store-id 作为 URL 路径的前缀)。%{REQUEST_URI} !\.\w{2,4}$
- 第二个条件避免重写对静态资源(图片、JS、CSS等)的请求。具体来说,它排除了任何以 - 看起来像 - 文件扩展名结尾的请求。%{HTTP_HOST} ^([^.]+)
- 第三个 条件 在 TLD 之前捕获请求的域名,之后可以使用%1
反向引用访问并用于作为 URL 路径的前缀。这假设没有 www 子域(如评论中所述)。例如。给定store1.com
或store2.co.uk
的请求,分别捕获store1
或store2
。
进行比较%{REQUEST_URI}@%1 !^/([^/]+)/.*@
- 第四个 condition 检查 URL-path 是否已经带有域名前缀(上面捕获的) ).这主要是为了保证重写的请求不会被再次重写,造成重写循环。这是使用内部反向引用 (</code>) 实现的,该反向引用将 URL 路径中的第一个路径段与先前捕获的域名 (<code>%1
).
前加上域名前缀(.*) %1/
- 最后,请求在内部重写,在请求的 URL-path.
忽略现有文件(可能不需要)
# ignore existing directories RewriteCond %{REQUEST_FILENAME} -d RewriteRule .* - [L]
您没有说明如何引用您的静态资源(图像、JS、CSS 等),但您可能需要修改此规则以匹配对现有文件的请求。 (尽管我在上面的规则中添加的条件可能已经足以排除这些请求。)例如:
# ignore existing directories or files
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
规范重定向(可选)
您还应该考虑 重定向 形式 store1.com/store1/catalogue/instruments
的任何直接请求返回规范 URL store1.com/catalogue/instruments
- 如果这些 URL 永远是 exposed/discovered。 store1.com/store2/catalogue/instruments
形式的请求自然会导致 404,所以这不是问题。
例如,以下内容将紧跟在您现有规则中的 ErrorDocument
指令之后:
# Redirect to remove the "/store1" URL-prefix when the "store1.com" domain is requested.
# - Only applies to direct requests.
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTP_HOST} !^domain\. [NC]
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 ^/([^/]+)/.*@
RewriteRule ^(?:[^/]+)(/.*) [R=301,L]
首先使用 302(临时)重定向进行测试,以避免潜在的缓存问题。
这基本上是上述重写的反向,但仅适用于直接请求。针对 REDIRECT_STATUS
env var 的检查可确保仅处理来自客户端的直接请求,而不处理后来重写的重写请求。
总结
有了两个规则块...
# permalinks
RewriteEngine on
# errors
ErrorDocument 404 /error.php?error=404
# Redirect to remove the "/store1" URL-prefix when the "store1.com" domain is requested.
# - Only applies to direct requests.
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTP_HOST} !^domain\. [NC]
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 ^/([^/]+)/.*@
RewriteRule ^(?:[^/]+)(/.*) [R=301,L]
# ignore existing directories
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Internally rewrite the request when a custom domain is used
# - the URL-path is prefixed with the domain name
RewriteCond %{HTTP_HOST} !^(www\.)?domain\. [NC]
RewriteCond %{REQUEST_URI} !\.\w{2,4}$
RewriteCond %{HTTP_HOST} ^([^.]+)
RewriteCond %{REQUEST_URI}@%1 !^/([^/]+)/.*@
RewriteRule (.*) %1/
#################################################
# store
:
: existing directives follow
: