How to add multiple domains to different pages on our website?

Hi there,

I just started using Grav! We try to create an multi-site setup.
We have around 20 domains which we want to link to one installation, with one theme (single-page). For every domain we want one page inside Grav.

We currently have the following setup:

domain-a,com -> Alias to base-domain,com
domain-b,com -> Alias to base-domain,com

base-domain,com
This domain contains the Grav installation. For each domain there is a page.

  • base-domain,com/domain-a,com
  • base-domain,com/domain-b,com

.htaccess
We made the following changes to our .htaccess. This is only for domain-a,com. We plan to make this dynamic, so that the requested domain always is appended to the redirect. But first we need to get the static version to work.

RewriteCond %{HTTP_HOST} ^domain-a\.com$
RewriteRule ^$ https://base-domain\.com/domain-a\.com? [NC]

The result is the base-domain,com root website delivered at domain-a,com.

When I change [NC] to [L,R=301] (basic 301 redirect), you get redirected to base-domain.com/domain-a.com and get the right page. But of course we don’t want to redirect.

Does someone have an idea how to solve this? Should it be done with the setup file in the root? I looked into it, but I can’t find a solution.

Andreas F

PS: Sorry for all the comma’s in the domain names. As a new user I could only place 2 url’s :confused:

1 Like

To get a little more information from you: youhave followed what steps are available at https://learn.getgrav.org/advanced/multisite-setup, correct?

And to have an understanding of your host, are you hosting on a Linux/Apache/PHP (DSO, SuPHP, FCGI, FPM) server with root access?

I haven’t made a Grav multisite install as of yet. (I have done so with WordPress and DNS/Apache/NGINX.) The good news is that Grav should adopt whatever domain requests its files, so we should simply be able to add new Apache aliases for the site on that end of things, but for Grav to display site specific content (different site title), based on my understanding, we would need to have the multisite setup.php above.

If all you are wanting is that we have one site with no change in content, we should be able to do everything from within Apache (or Nginx/LiteSpeed) and DNS.

Is it necessary that visitors hit domain.com/domain-a.com or can they navigate to domain.org and domain.net and domain.com and see the exact same content as domain.com? In other words, can we have a parked domain or must the site serve some variation in content?

I did not follow the https://learn.getgrav.org/advanced/multisite-setup guide. The official multisite setup requires multiple themes for each site. I really cannot do this because we want to run more than 20 domains for the same theme. It would be a maintenance nightmare .

I’m indeed running Apache, PHP7 FPM on Linux (CentOS).

Visitors don’t need to access base-domain,com/domain-a,com. This website is just there to provide the installation.

For each domain we’ve created a single page on the base-domain. We just need to point those domains to the page. (See the .htaccess)

We followed the multisite setup and having things working with a shared themes folder (and shared plugins folder). Like you, we only want 1 theme for all sites (we also only want 1 set of plugins). For us, the multisite setup is working really well. Here’s our setup.php file:

<?php
/**
 * Multisite setup for sub-sites accessible via sub-domains.
 */

use Grav\Common\Utils;

// Get sub-site name from sub-domain
$environment = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
// Remove port from HTTP_HOST generated $environment
$environment = strtolower(Utils::substrToString($environment, ':'));
$folder = "sites/{$environment}";

if ($environment === 'localhost' || !is_dir(ROOT_DIR . "user/{$folder}")) {
    return [];
}

return [
    'environment' => $environment,
    'streams' => [
        'schemes' => [
            'user' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ["user/{$folder}"],
                ]
            ],
            'cache' => [
                'type' => 'Stream',
                'prefixes' => [
                    '' => ["user/{$folder}/cache"],
                ]
            ],
            'plugin' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ['user/plugins'],
                ]
            ],
            'plugins' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ['user/plugins'],
                ]
            ],
            'config' => [
                'type' => 'ReadOnlyStream',
                'paths' => [
                    'user/config',
                    'system/config',
                ],
                'prefixes' => [
                    '' => ["user/{$folder}/config"],
                ]
            ],
            'themes' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ['user/themes'],
                ]
            ],
        ]
    ]
];
1 Like

Thanks! We’re almost there!

Now I’ve to create additional user accounts for each site to use the Admin plugin. I would like to have one single account. Any idea how to do this?

I tried adding the following, but it keeps looking at the accounts folder of the site…

       'account' => [
            'type' => 'ReadOnlyStream',
            'paths' => [
                'user/accounts'
            ]
        ]

So the users should be received from user/accounts instead of user/sites/domain.com/accounts.

Is there any documentation about which streams are available inside Grav? I cannot seem to find any? Or

The only way I could get this to work was by adding the following to user/config/streams.yaml:

schemes:
    account:
        type: ReadOnlyStream
        paths:
            - 'user/accounts'

This was very confusing for me as well but give it a try and hopefully it does the trick for you.

1 Like

Awesome! It is indeed confusing and there is not a lot of documentation for this. I don’t get why this part should be configured in yaml, while others have to be configured in the setup.php.

Do you also know of a way to make it possible to edit all sites in the backend of one site? Now the user has to login on each site.

I could make a redirect for the admin page to my base domain. But I’m still not sure how to include all sites there.

Hopefully this will someday become a feature but for now I don’t think there’s any easy way to do this (I’m rather new to Grav so I could certainly be missing something).

One idea for this feature would be to have a site level in the page hierarchy that you see on the manage pages screen. You would first see a list of all sites, then you would expand a site and see the pages for that site.

I just hacked around the issue a bit. We have one domain we use as a base domain.

We redirect to the base domain if the admin page is accessed on another domain. This way the user doesn’t have to login for each site. We store the chosen site in a cookie which is used as envoirement.

As I’m writing this, I noticed this has a security flaw. You could easily change the environment to “…/…/system” for example. But this should be easy to fix.

Our setup.php

<?php
use Grav\Common\Utils;

function startsWith($haystack, $needle)
{
     $length = strlen($needle);
     return (substr($haystack, 0, $length) === $needle);
}

// Get sub-site name from sub-domain
$environment = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost');
// Remove port from HTTP_HOST generated $environment
$environment = strtolower(Utils::substrToString($environment, ':'));

$isAdmin = startsWith($_SERVER['REQUEST_URI'], "/admin");

if($environment != "base-domain.com" && $isAdmin){
    header("location: https://base-domain.com/admin?site=" . $environment);
    exit();
}
else if($isAdmin && isset($_GET['site'])){

    setcookie('tsite', $_GET['site'], 0, "/admin");
    $environment = $_GET['site'];
}
else if (isset($_COOKIE['tsite'])) {
    $environment = $_COOKIE['tsite'];
}


$folder = "sites/{$environment}";

if ($environment === 'localhost' || !is_dir(ROOT_DIR . "user/{$folder}")) {
    return [];
}


return [
    'environment' => $environment,
    'streams' => [
        'schemes' => [
            'user' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ["user/{$folder}"],
                ]
            ],
            'cache' => [
                'type' => 'Stream',
                'prefixes' => [
                    '' => ["user/{$folder}/cache"],
                ]
            ],
            'plugin' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ['user/plugins'],
                ]
            ],
            'plugins' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ['user/plugins'],
                ]
            ],
            'config' => [
                'type' => 'ReadOnlyStream',
                'paths' => [
                    'user/config',
                    'system/config',
                ],
                'prefixes' => [
                    '' => ["user/{$folder}/config"],
                ]
            ],
            'themes' => [
                'type' => 'ReadOnlyStream',
                'prefixes' => [
                    '' => ['user/themes'],
                ]
            ],
        ]
    ]
];