In this example, I manage domain.com
. Inside it, I have a store template in php that loads the selected store dinamically:
domain.com/store1
domain.com/store2
domain.com/store3
The nice urls are being generated by the .htaccess
below. What's really being loaded in the back, is this:
domain.com/store/index.php?store=store1
domain.com/store/index.php?store=store2
domain.com/store/index.php?store=store3
Each store has internal links, such as these:
domain.com/store1/catalogue
domain.com/store1/catalogue/instruments
domain.com/store1/catalogue/instruments/guitars
domain.com/store1/electric-guitar/1337
It works exactly as expected. These are the .htaccess
rules to make that work in domain.com
. The store
query string (store1
, store2
, store3
) in each RewriteRule
determines which store is being loaded:
# 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=$1 [QSA,L]
# store: catalogue
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/?$ store/catalogue.php?store=$1 [QSA,L]
RewriteRule ^([0-9a-zA-Z-]{2,50})/catalogue/([0-9a-zA-Z-]{1,75})/?$ store/catalogue.php?store=$1&cat=$2 [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=$1&cat=$2&subcat=$3 [QSA,L]
# store: products
RewriteRule ^([0-9a-zA-Z-]{2,50})/([0-9a-zA-Z-]{1,150})/([0-9] )$ store/product.php?store=$1&slug=$2&id_product=$3 [QSA,L]
Now, here comes the tricky part. Some of the clients, would like to use their own domains, so that store1.com/*
loads the same content of domain.com/store1/*
(without using a redirect).
Example:
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
You get the idea.
All the domains (domain.com
, store1.com
, store2.com
, store3.com
) are configured in the same Apache Web Server environment (using virtual hosts).
The question is: Can this be implemented in a .htaccess
file in each one of the store's domain root path dynamically or inside the virtualhost .conf
file? If so, how?
CodePudding user response:
Make sure all your custom "store" domains (eg. store1.com
, store2.com
, etc.) are defined as ServerAlias
in the domain.com
vHost and so resolve to the same place as domain.com
.
You can then rewrite requests for the custom store domain to prefix the URL-path with the store-id (the domain name) and then use your existing rules unaltered.
For example, a request for store1.com/catalogue/instruments
is internally rewritten to /store1/catalogue/instruments
and then processed by the existing rules as usual.
The following directives should go before the existing # store
rules:
# 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 !^/([^/] )/.*@\1
RewriteRule (.*) %1/$1
And that's basically it, although you may also decide to implement a canonical redirect - see below.
Explanation of the above directives:
The first condition simply excludes requests for the main
domain.com
(which should already have the relevant store-id prefixed to the URL-path).%{REQUEST_URI} !\.\w{2,4}$
- The second condition avoids rewriting requests for static resources (images, JS, CSS, etc.). Specifically, it excludes any request that ends in - what looks like - a file extension.%{HTTP_HOST} ^([^.] )
- The third condition captures the requested domain name before the TLD which is then accessible using the%1
backreference later and used to prefix the URL-path. This assumes there is no www subdomain (as stated in comments). eg. Given a request forstore1.com
orstore2.co.uk
,store1
orstore2
respectively are captured.%{REQUEST_URI}@%1 !^/([^/] )/.*@\1
- The fourth condition checks that the URL-path is not already prefixed with the domain name (captured above). This is primarily to ensure that rewritten requests are not rewritten again, causing a rewrite loop. This is achieved using an internal backreference (\1
) that compares the first path-segment in the URL-path against the previously captured domain name (%1
).(.*) %1/$1
- Finally, the request is internally rewritten, prefixing the domain name to the requested URL-path.
Ignore existing files (may not be required)
# ignore existing directories RewriteCond %{REQUEST_FILENAME} -d RewriteRule .* - [L]
You've not stated how you are referencing your static resources (images, JS, CSS, etc.), but you may need to modify this rule to also match requests for existing files. (Although the condition I added to the rule above may already be sufficient to exclude these requests.) For example:
# ignore existing directories or files
RewriteCond %{REQUEST_FILENAME} -d [OR]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
Canonical redirect (optional)
You should also consider redirecting any direct requests of the form store1.com/store1/catalogue/instruments
back to the canonical URL store1.com/catalogue/instruments
- should these URLs ever be exposed/discovered. A request of the form store1.com/store2/catalogue/instruments
will naturally result in a 404, so is not an issue.
For example, the following would go immediately after the ErrorDocument
directive in your existing rules:
# 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 ^/([^/] )/.*@\1
RewriteRule ^(?:[^/] )(/.*) $1 [R=301,L]
Test first with a 302 (temporary) redirect to avoid potential caching issues.
This is basically the reverse of the above rewrite, but applies to direct requests only. The check against the REDIRECT_STATUS
env var ensures that only direct requests from the client and not rewritten requests by the later rewrite are processed.
Summary
With the two rule blocks in place...
# 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 ^/([^/] )/.*@\1
RewriteRule ^(?:[^/] )(/.*) $1 [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 !^/([^/] )/.*@\1
RewriteRule (.*) %1/$1
#################################################
# store
:
: existing directives follow
: