Dynamically pointing multiple domains to a single domain's subfolder with .htaccess - apache

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?

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 for store1.com or store2.co.uk, store1 or store2 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
:

Related

Apache RewriteRule pass entire URL as a parameter

Currently my Apache RewriteRule only passes the path of the original URL as a query parameter to the rewritten URL.
How do I send the entire URL (including scheme and authority etc) as a parameter to the rewritten URL?
I know %{REQUEST_URI} only passes the path, and I can’t see any Apache environment variables that do pass the entire URL.
I’m something of a beginner to Apache configuration so excuse any ignorance on my part,
thanks!
Here’s my current config:
#
# Enable Rewrite
#
RewriteEngine On
#
# Condition for rewriting the URL. Check the URL's path is a valid REST URI path
#
RewriteCond %{REQUEST_URI} ^(?:[^?#]*)(?:/v[0-9]+(?:\.[0-9]+)*)(?:/[a-z]+)(?:/[a-z]+)(?:/?[^/?#]*)(?:[^?#]*)$
#
# Rewrite the URL, sending the REST path as a parameter to the specified version.
#
RewriteRule ^(?:[^?#]*)(?:/(v[0-9]+(?:\.[0-9]+)*))((?:/[a-z]+)(?:/[a-z]+)(?:/?[^/?#]*)(?:[^?#]*))$ https://localhost/rest/$1/index.php?request_uri_path=https://localhost/rest/$1/$2 [QSA,NC,L,R=307]
Currently I’ve hardcoded the url into the query parameters so the URL
https://localhost/rest/v1/users/show/12345
becomes:
https://localhost/rest/v1/index.php?request_uri_path=https://localhost/rest/v1/users/show/12345
You can use this rule to get full URL as query parameter:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{HTTPS}s on(s)|
RewriteRule ^ index.php?request_uri_path=http%1://%{HTTP_HOST}%{REQUEST_URI} [L,QSA]
If you're on Apache 2.4 then you can make use of %{REQUEST_SCHEME} variable:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php?request_uri_path=%{REQUEST_SCHEME}://%{HTTP_HOST}%{REQUEST_URI} [L,QSA]

Configure htaccess for url rewriting

I need help for my url's rewriting. I want to organize my url's precisely by taking account of the current organization of my current tree:
-! /www
.htaccess,
index.php, ...
--! **/views**, /assets
pageA.php, pageB.php, pageC.php
---! **/pageA**, /pageB, /pageC, /errors (404.php, 500.php...)
pageA1.php, **pageA2.php**, pageA3.php
/!\ I use specific names for each pages, the prefixes and numbers are just for examples and for a better comprehension about connexions between files and folders. In reality I use pages names like: "contact-us.php", "our-product.php" ...
(1) When I going to pageA.php I would like this url path: www.mywebsite.fr/pageA
(2) When I going to pageA2.php, I would like: www.mywebsite.fr/pageA/pageA2
(3) I don't want extension files (.php, .html)
Actually, I can't go into the path (2) cause I have a 404 page.
I created folders with the same name as a specific php pages it's just for my own organization, but it can be a bad way (for SEO or anything else...) in fact, I don't know...
My rewrite module (in .htaccess of www) is:
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine on
RewriteBase /
# Retirer les extensions des pages et les rendres accessibles en lecture
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.php
RewriteRule ^/?(.*)\.php$ /$1 [L,R=301]
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php [L]
# Suppression d'un sous répertoire
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/views/$1.php -f
RewriteRule (.*) /views/$1.php [QSA,L]
</IfModule>
This is just off the top of my head, but it should work:
RewriteRule ^page([A-Z]{1})([1-9]?).php$ http://www.mywebsite.fr/page$1$2
RewriteRule ^page([A-Z]{1})([1-9]{1})$ http://www.mywebsite.fr/page$1/page$1$2 [L,R=301]
The first Rewrite directive checks for a filename match starting with
page
and ending (prior to the filename extension, .php) with
a single letter (from A to Z); and
(optionally) a single number (from 1 to 9)
If it does, it captures both the letter (and the optional number) and rewrites the URI without the .php file extension at the end.
For those URIs which do include the optional number, the second Rewrite directive captures both the letter and the number separately from each other.
It then builds the redirect according to the correct folder structure, deploying both the captured letter and the captured number.

change file name with mod_rewrite?

If you have a file /foo/index.html that you want to access via /bar/index.html that's easy enough with a rewrite:
RewriteRule ^bar/(.*)$ foo/$1 [L] # <-- Leaves URL the same, but gives content of foo/
But that means both /foo/index.html and /bar/index.html now work to access the content. Is there a way to now disallow access via /foo/index.html? Just doing:
RewriteRule ^foo/(.*)$ bar/$1 [R=301,L] # <-- Change the URL if accessed via /foo
RewriteRule ^bar/(.*)$ foo/$1 [L] # <-- Leaves URL the same, but gives content of foo/
causes a redirect loop. Is there a way to indicate "redirect foo to bar, unless the URL is already '/bar'"?
Yeah, but you need to do a check against something like %{THE_REQUEST} otherwise your two rules are going to keep changing each other's rewrites and loop. So something like:
# this is the rule you already had:
RewriteRule ^bar/(.*)$ foo/$1 [L]
# this is the rule that externally redirects
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /foo/
RewriteRule ^foo/(.*)$ /bar/$1 [L,R=301]
The check against the %{THE_REQUEST} variable ensures that the actual request was made for /foo, and not an internally rewritten request.

How to redirect dynamic pages using htaccess

I have moved site from one framework to another and now I would like to redirect important pages (around 20). How to redirect them using apache rewrite? Here are examples:
Old: http://mydomain.com/ba/stream.php?kat=15
New: http://www.mydomain.com/bs/about-us/our-partners
Old: http://mydomain.com/ba/stream.php?kat=29
New: http://www.mydomain.com/bs/catalogues/it-catalogue
Also, after important pages are listed, I'd like to redirect all remaining links in form:
http://mydomain.com/ba/whatever-is-here
to
http://www.mydomain.com/bs/
Following TerryE's suggestion, I'll attach my latest code which is not working :)
RewriteCond %{QUERY_STRING} ^kat=15$
RewriteRule ^stream\.php$ http://www.mydomain.com/bs/about-us/our-partners [R=301,L]
One more note: I use this code to redirect root domain to www subdomain:
RewriteCond %{HTTP_HOST} ^mydomain\.com$ [NC]
RewriteRule ^(.*)$ http://www.mydomain.com/$1 [R=301,L]
I have placed this root to www code below code for specific pages redirection, and when I test http://mydomain.com/ba/stream.php?kat=15 in browser, I am redirected to http://www.mydomain.com/bs/ba/stream.php?kat=15
Adnan,
You need to add the following lines to your DOCROOT/.htaccess. This assumes that you don't have any .htaccess file in your ba subdirectory. (See Tips for debugging .htaccess rewrite for an explanation of why.)
RewriteEngine On
RewriteBase /
#
# Redirect kat 15 to the Partners page
#
RewriteCond %{QUERY_STRING} ^kat=15$
RewriteRule ^ba/stream\.php$ http://www.mydomain.com/bs/about-us/our-partners? [R=301,L]
#
# Redirect all other kat ids to the corresponding bs page
#
RewriteCond %{QUERY_STRING} ^kat=(\d+)$
RewriteRule ^ba/stream\.php$ http://www.mydomain.com/bs/%1? [R=301,L]
Note:
the rule matches against the UPI less the leading path (in this case /)
you need to have the trailing ? on the replacement string to suppress the existing query parameters.
the %n parameters pick up the match variables from the last successful condition match.

htacces rewrite tld without changing subdomain or dirs

I am trying to rewrite the following url:
the subdomain should match any subdomain. same for the TLD.
both: http://car.example.com/ and http://cat.example.co.uk should be rewritten
http://subdomain.example.com/some/dir
to
http://subdomain.example.nl/some/dir
and
http://example.com/some/dir
to
http://exampkle.nl/some/dir
(also with www. adress)
but my knowledge of htaccess and rewrite rules in general aren't good enough for this :(
I hope one of you knows the solution.
ps. I did try a search ;)
The challenge comes with having to detect and account for four different possible domain patterns:
example.com → example.nl
example.co.uk → example.nl
sub.example.com → sub.example.nl
sub.example.co.uk → sub.example.nl
So, what this ruleset does is checks that the TLD is not .nl (preventing a loop from occurring), then pulls the subdomain, www or not, off the front (read as "capture anything other than a dot followed by a dot, optional), followed by the base domain, followed by a dot. We don't have to match the entire URL, since we aren't keeping the TLD.
RewriteEngine On
RewriteCond %{HTTP_HOST} !example\.nl$
RewriteCond %{HTTP_HOST} ^([^.]+\.)?example\.
RewriteRule ^ http://%1example.nl%{REQUEST_URI} [NC,L,R=301]
The RewriteRule's ^ matches any URL, then inserts the contents of the first set of parens in the preceding RewriteCond (the subdomain) with %1, and completes the rewriting by appending the requested path and flags to ignore case, make this the last rule, and redirect with a search-engine-friendly 301, ensuring the rewritten URL appears in the user's browser. Any query string (text appearing after a ? in the URL) is automatically included by default.
Try this:
EDIT: See changes to subdomain, using %1 to capture from RewriteCond
RewriteEngine On
# Check if the hostname requested is subdomain.example.com or empty
# Now we attempt to capture the subdomain with (.*)? and reuse with %1
RewriteCond %{HTTP_HOST} ^(.*)?example.com$ [NC]
RewriteCond %{HTTP_HOST} !^$
# Rewrite it as subdomain.example.nl and redirect the browser
RewriteRule ^(.*) http://%1example.nl$1 [L,R,NE,QSA]
# Note: With the above edit for %1, this part should no longer be necessary.
# Then do the same for example.com, with or without the www
RewriteCond %{HTTP_HOST} ^(www\.)?example.com$ [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^(.*) http://www.example.nl$1 [L,R,NE,QSA]