Building Your Startup: Running Multiple Domains

Final product image
What You'll Be Creating

This tutorial is part of the Building Your Startup With PHP series on Envato Tuts+. In this series, I'm guiding you through launching a startup from concept to reality using my Meeting Planner app as a real-life example. Every step along the way, I'll release the Meeting Planner code as open-source examples you can learn from. I'll also address startup-related business issues as they arise.

Launching a Second Web Domain

Expanding awareness and usage of Meeting Planner is my greatest challenge right now. Without lots of input, it's hard to make the product better, and without fast growth, it's hard to attract investors.

I've had concerns that the brand Meeting Planner might lead to people misunderstanding the app's social usefulness, such as planning friendly get-togethers, dates, and parties.

Obviously, choosing names is limited by domain availability and/or the budget you have to invest in buying alternatives. Meeting Planner seemed best at the time.

Recently, I noticed was available, so I registered it and began to integrate the domain alongside the existing Meeting Planner service. 

There are a couple of different approaches to adding domains to a Yii2-based application. In today's tutorial, I'll cover the simplest, running domains on the same codebase.

If you haven't tried out Meeting Planner yet, go ahead and schedule your first meeting at Simple Planner. I do participate in the comment threads below, so tell me what you think! You can also reach me on Twitter @reifman. I'm especially interested if you want to suggest new features or topics for future tutorials.

As a reminder, all of the code for Meeting Planner and Simple Planner is written in the Yii2 Framework for PHP. If you'd like to learn more about Yii2, check out our parallel series Programming With Yii2.

Before I get a started, I'd like to touch on one aspect of the challenge of building a startup.

A Peek at Startup Life

I've been working on this episode over the weekend, a "perk" of #StartupLife. I wanted to share a couple of fun things about this. 

First, #StartupLife often means not having the time to finish putting together IKEA Furniture, just the biggest drawer:

Building Your Startup Multiple Domains - Photo of my IKEA dresser with one drawer built and others not built

Second, it also means working at coffee shops in fun places... Five Point Roasters in Portland, Oregon happens to have a neighboring bubble machine (or people breathe bubbles there):

Now, back to the focus of today's tutorial...

Implementing Multiple Domains in Yii

The Yii2 advanced template allows you to run a number of sites in one code tree. I used its front-end tree to build Meeting Planner and its back-end tree to build the administrative suite of tools for the service. Today, however, I'll focus on launching another domain on top of the existing front-end tree—and all the small and large complexities that go along with this. 

In a future episode, I'll cover building sites on a third or fourth tree, for example a REST API or a related startup to Meeting Planner (yes, there's an exciting idea ahead).

I presumed it would be fairly simple (no pun intended) to launch Simple Planner, but it ended up taking a few days of work.

The simple work was the basic server configuration and some code handling with the Yii tree. However, since Meeting Planner is an email-intensive service (meeting invitations, confirmations, announcements, updates, etc.), it was important to send these emails with the domain from which meetings were created, either or

I also wanted there to be some initial visual differentiation between the two sites.

Let's get started, and I'll gradually reveal some of the complexities I encountered.

Configuration Based on the Domain Address

When people arrive at our site via browser requests, we need a central way in Yii to instruct all executed code which service should be shown: Meeting Planner or Simple Planner?

In /frontend/config/main.php, there is an unfortunately named bootstrap configuration (because it overlaps with Bootstrap) which will run code at the beginning of the framework invocation:

In addition to calling logging above, I created a component for it to call named SiteHelper:

So SiteHelper has all the code to customize the service based on which site is running. For example:

Those functions change variables, URLs and strings based on the requested service. These are called by the SiteHelper::init() function:

The above functions also override settings when calling from localhost:8888 development sites.

Meeting Planner is SITE_MP, Simple Planner is SITE_SP, and SITE_FD is my secret from you (for now).

Unique Home Page Appearance

For now, I decided to quickly vary the appearance of Simple Planner (SP) and Meeting Planner (MP) by using Bootstrap 3.0's two default navigation bars.

You'll notice above MP uses Yii::$app->params['site']['navbar'] = 'navbar-default'; and SP uses 'navbar-inverse';.

In /frontend/views/layouts/home.php and main.php, they are applied this way:

This creates the two different navbar color schemes:

Building Your Startup Multiple Domains - Side by side looks at Simple Planner vs Meeting Planner home page

But what about the cover images? I licensed two playful social images for SP and two more serious professional images for MP. 

The images are rotated randomly depending on the active service, with image filenames ending numerically as 0, 1, 2, or 3, e.g. /img/home/home-#.jpg.

Here's the home.php layout code that applies this to the chosen image:

If you refresh the home pages for or, you'll see the images oscillate.

Updating Text, Images, and Links

Variables from SiteHelper above help customize textual labels throughout the site. And in the future, I can do this more extensively:

While MP calls things Meetings, I could globally change SP to use the more social phrase Meetups:

Configuring Services

Meeting Planner uses a lot of different services to deliver scheduling. Initializing these took the most time.

Google Analytics and Search Console

Google makes it a bit difficult to use multiple domains with Analytics, so I just divided them into different accounts. The SiteHelper sets up the GA code:

Then, these are set in the Home.php and Main.php layout views:

There's also a domain setting for Google's Search Console which I configured at my registrar (shown further below):

Building Your Startup Multiple Domains - Google Search Console Verification TXT Record

OAuth Login

And, since I added social login via OAuth for Facebook, Google, and LinkedIn, I had to tell all of these services about the domain. Google and LinkedIn are the most confusing because each and every query argument variation must be registered with those geniuses:

Here's Google:

Building Your Startup Multiple Domains - Google Developer Credential URLs

Here's the simple LinkedIn:

Building Your Startup Multiple Domains - LinkedIn OAuth Configuration URLs

But Facebook is the simplest and least picky (just keep the breastfeeding pics to a minimum):

Building Your Startup Multiple Domains - Facebook Developers Configuration URL

Mailgun Email

Since I use Mailgun to deliver emails, I created a domain account for Simple Planner with them:

Building Your Startup Multiple Domains - Domain Configuration Requirements from Mailgun

This required careful changes over at my domain registrar:

Building Your Startup Multiple Domains - Domain Registrar DNS Settings for Mailgun


I use Let's Encrypt for the https (SSL) with Meeting Planner and its back-end administrative service. Setting those up went pretty smoothly for me. But adding didn't work, and I ultimately had to create separate Apache (.conf) configuration files for SP and customize the files myself.

Here's the http sp.conf file which redirects to https:

The redirections just weren't working with two server names and two server aliases. Here's the sp-ssl.conf file:

Here's how I ultimately created the SSL certificates with Let's Encrypt:

Let's Encrypt is awesome, but they are still young, and while their scripts continue to get more robust, there are still issues occasionally like this one.

Background Email Differentiation

The biggest challenge for me in launching Simple Planner was processing background emails so that they came from the proper service, i.e. MP or SP, and all of their links did as well.

Since I kept the database unified for SP and MP, I also left the background processing to be done on the MP domain. So I had to extend the database so the User and Meeting table saved a site_id column to indicate which service created each entry.

Here's the Yii database migration:

I used the beforeSave() method in the Meeting model to always configure a site_id based on the current service—you may remember I created this method to always generate a secure unique identifier for every meeting:

I created a similar one for the User model:

Throughout the service I use the MiscHelpers::buildCommand() method to construct secure links that don't require users to log in each time they respond to an email.

I thought it would be easy to customize this command in one place to link to the appropriate site domain. However, that would require that this frequently used method query the Meeting table for the site_id repeatedly. For example, the buildCommand() is called a number of times for every participant for every meeting.

For performance reasons, I decided to change all the calls to this function to include the site_id. For example, all of these calls had to be amended as shown below:

The Meeting model is always loaded prior to these calls, so accessing the site_id is fastest from here.

What's Next?

Building Your Startup Multiple Domains - SimplePlannerio Home Page with Social Image

While all the configurations and small variable changes of this site got a bit annoying to build after a while, it shows off some pretty cool features of the Yii Framework. If you haven't yet, please try scheduling at the new site, Simple Planner

Share Meeting Planner with your business associates and Simple Planner with your friends and family.

Have your own thoughts? Ideas? Feedback? You can always reach me on Twitter @reifman directly. Watch for upcoming tutorials here in the Building Your Startup With PHP series.

I'm also getting closer to launching the experiment with WeFunder based on the implementation of the SEC's new crowdfunding rules. You can follow our profile there if you'd like. I will also write more about this in a future tutorial.

Related Links



Related Articles