Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Adjust HTTP, SSL or AJP bindings per site using CommandBox Multi-Site
HTTP bindings are much more robust in Multi-Site mode. In single-site mode, there is basically no such thing as host name bindings. All traffic coming into that CommandBox server is simply routed to the one and only server regardless of its host name or IP address.
This page covers bindings from a Multi-Site perspective, but the general docs on all Binding options is here.
As of version 6.0, a CommandBox server can now have
Any number of HTTP bindings on any IP/port combination
Any number of SSL bindings on any IP/port combination
Any number of AJP bindings on any IP/port combination
And furthermore, a binding can be shared for all sites on the server, or could serve traffic to ONLY a single site depending on how you configure it. Think how IIS, Nginx or Apache work. You now have the same level of flexibility in CommandBox!
CommandBox 6 has introduced a new syntax for bindings (and is still backwards compatible with the old JSON syntax too) that allows you to specify hostnames alongside the bindings:
Even though each site can use a different IP/port, a very common setup is to have a single port 80
/443
binding at the server level shared by all sites, and then simply specify a hostname for each site. If you only need to specify a host name for a site, but don't need to declare a new binding (because you're inheriting a binding from the global level) then you can use the new hostAlias
key at the site level which can be a list or array of hostnames to be layered on top of the global bindings for this site:
In Multisite mode, each site can have as many bindings as you want, where each binding has:
An IP address (or all IPs, which is the equivalent to 0.0.0.0
)
A port
Zero or more hostnames
Exact (full) match like www.foo.com
Starts-with match like *.foo.com
or *bar.com
Ends-with match like www.foo.*
or www.bar*
Regular expression match like ~www\.(bob|tom|bernard)Industries\.(com|net|org)
When a request comes into CommandBox in multi-site mode, the most specific binding will be found, and the traffic served to the matching site. The order of precedence of binding matching is as follows:
Exact IP and hostname match
Exact IP and hostname ends with match
Exact IP and hostname starts with match
Exact IP and hostname regex match
Any IP and hostname exact match
Any IP and hostname ends with match
Any IP and hostname starts with match
Any IP and hostname regex match
Try Exact IP and any hostname
Any IP and any hostname
Default site
If you need help debugging how your bindings are being assembled at runtime, you can use this command:
which will show you all the listeners (ports/IPs) that CommandBox is listening to at the OS level as well as all the bindings for each site that control what listeners will map to them:
If you do not specify any bindings at all, it will still pick a random port to bind to. It will also pick a SEPARATE random port for each site so you can access each site separately.
Each site can have a default open browser URL, but CommandBox will not automatically open a browser in Multi-Site mode. These URLs will be available in a sub menu of the tray icon, or can be used from the server open
command like so:
If no bindings were matched, then CommandBox will look for a default site configuration. Unlike IIS which has a default site created by scratch, or Apache which just takes the first site defined, CommandBox requires you to explicitly define a default site like Nginx. If no default site is defined, CommandBox has a built in "site not found" error page that will be shown. You can avoid or "turn off" this default page by setting a default site.
Configure any of your sites to be the default site by adding a default
key set to true
inside the site JSON. It doesn't matter whether the site is defined in the server.json
in the sites
object or in an external site JSON file.
All the Multi-Site examples you need to get started with Multi-Site
This Github repository has several Multi-Site examples in the folders starting with the words multi-site-
. Here is an overview of a few of them.
Here is a basic server.json
configuring several sites. Here we have some defaults configured in the web
object for all sites, including a single HTTP binding to all IPs on port 80. There is a custom host alias configured on each site. There is a site called "default" which is set to the default site with default=true
in the site object.
Full example here: https://github.com/Ortus-Solutions/commandbox-tests/tree/master/multi-site-basic
This example server contains custom HTTP and SSL bindings for each server. You'll probably never need anything this complex, but we support it all! The HTTP port 81 binding on all IPs, SSL binding on port 444, and AJP binding on port 8010 are shared by all sites. Each site then adds additional bindings specific to the site. Needless to say, there are multiple URLs that lead to each site.
Full example here: https://github.com/Ortus-Solutions/commandbox-tests/tree/master/multi-site-bindings
This example server configures some defaults in the web
object and then simply points to the web root of several sites where a .site.json
file is waiting to further configure each site:
And here are the .site.json
files from each of the web roots:
Full example here: https://github.com/Ortus-Solutions/commandbox-tests/tree/master/multi-site-json-webroot-convention
This example is similar to above, but instead of pointing to the web root of each site, it points to the site config file for each site, which is stored outside of the web roots:
In this example, the site1.json
, site2.json
, site3.json
, and default.json
files all live in the same directory as the server.json and would point to their respective webroots like so:
Full example here: https://github.com/Ortus-Solutions/commandbox-tests/tree/master/multi-site-siteConfigFile
In this example, we have a single file globbing pattern that points to a directory of JSON files that define the sites:
In this example, we have a folder called sites-enabled
in the same directory as the server.json
with one or more JSON files. Here is an example:
Note the relative paths are always relative to this JSON file.
Full example here: https://github.com/Ortus-Solutions/commandbox-tests/tree/master/multi-site-siteConfigFiles-globbing
Learn how to serve multiple sites with different web roots, rewrites, and more from a single CommandBox server
The default behavior when you run server start
is to start a server that points only to a single web root. There are two methods you can use to host more than one web root at a time in a single CommandBox server:
- this requires a web server sitting in front of CommandBox which sends special HTTP headers that instruct CommandBox what to use as the web root.
Multi-Site mode- this is new in CommandBox 6.0 and it allows full configuration of multiple sites (web roots), rewrite, HTTP/SSL bindings, and settings inside of a single server.
In both cases, there is a single JVM process and a single CF Engine at play. So all sites will use the same CF engine and version. If you want to run different versions of CF, you'll need more than one server. You can use a reverse proxy in your server rules to still access all your sites via a single "front end" server instance.
The CommandBox built-in web server is now fully fledged with out-of-the-box support for multiple bindings, host header matching, multiple SSL certs/SNI, and full control over rewrites gzip compression, virtual directories (aliases) and secure profiles on a per-site basis. CommandBox is now truly the only tool you need to deploy your code! There's no need to have Apache, IIS, or Nginx in the mix any longer.
There is a repo on Github that contains working examples of all the features of Multi-Site. Feel free to skip straight there to learn by example.
You are free to use CommandBox as you wish for development purposes, but if you are using more than 2 sites in production, we ask you consider supporting us with a license which bundles support and commercial modules and extensions.
Please note that the using Tuckey will NOT work by default in Multi-Site mode. There are two main reasons for this limitation:
Tuckey is tied to the servlet and static files are now no longer served by the Default Servlet, but are returned to the browser without even touching the servlet.
There is only one servlet servicing all sites, so you can't have different rewrites per site.
Not ready to leave Tuckey? You can force the new Undertow web server to not serve static files by setting servletPassPredicate=true
, which will send ALL files to the servlet:
Read more about the .
However, the best approach is to simply switch over to using . They do everything Tuckey does, AND each site can get its own separate set of server rules for maximum configurability.
Also note, when you set this flag:
as of CommandBox 6, a Tuckey XML rewrite file will no longer be employed. Instead Server Rules will be added that perform the same rewrites as the old Tuckey functionality. Tuckey will now ONLY be used if you specify a custom rewrite file.
Defining sites in CommandBox Multi-Site
There's no flag or setting for enabling multiple sites in a single server. All you have to do is
define a sites
object in server.json
or a siteConfigFiles
setting in server.json
inside your server.json
and if one or more sites are found, CommandBox will automatically switch over to "Multi-Site" mode. It's easy to tell if a server has more than one site. When you start it, the console output will have a dedicated section in the output for each site. There is also additional information in the server info
command about each site.
Let's explore the two methods of defining more sites.
The sites
object will have a key for each site you want to define where the name of the key is the name of each site and the value is another object containing the configuration of that site. Think of all of the setting which can be set in the web
object of server.json
. Each site object in the sites
object can have all the same settings as the top-level web
object so it will be very familiar.
The simple example server.json
above, some defaults that apply to all sites are defined in the top-level web
object. Then we have two sites, site1
and site2
defined with their own settings. Remember, each site object is just a copy of the web
object above, so all the same settings are valid. You can use the server set
command to configure these and rely on the tab completion to help prompt you with all valid options.
The ONLY required settings for each site are
webroot
or siteConfigFile
which points to a JSON file on disk where a web root must be defined
If you want to access each site individually, you'll either want to specify a separate HTTP/SSL/AJP binding OR a custom hostname for each site. Otherwise, you'll have no way to access them!
You can also place a .site.json
file inside of the web root, which will be found by convention and its contents will be added to the site object for that site. This is akin to configuring your sites in Apache, but having an .htaccess
file in each web root to override settings.
System setting expansions will be processed in any .site.json
files, and if a .env
file exists in the web root as well, thoses env vars will be loaded, but JUST for that .site.json
file's expansions.
siteConfigFiles
setting in server.json
If you have a lot of sites, or a lot of settings, the first option can become unwieldly since the server.json
will have a lot of stuff in it. That is why we also allow you to specify a siteConfigFiles
setting which contains a list or array of file globbing patterns or directories to look for JSON files in. Once all the JSON files have been found, they will be used to define sites. each JSON file will contain JUST the contents of the object you would have put in the sites
object of server.json
which has all the same options as a web
object in server.json
.
Valid examples could be:
System setting expansions will be processed in any of the site JSON files located via your file globbing patterns. If the server.json
has a sites
object AND a siteConfigFiles
setting, all sites will be combined together. Feel free to choose the approach that fits your needs!
Use a servlet pass predicate to define which requests should be forwarded to the servlet
In a traditional server setup, you are used to two separate pieces of software
A web server like NGinx, Apache, or IIS which serves static files, performs URL rewrites, and proxies some requests off to a servlet
A Servlet container like Tomcat or CommandBox receiving the proxied requests on another port (HTTP or AJP) and processing CF code
In CommandBox, now we have the ability to combine both of those bullets together into a single piece of software. No reverse proxy is needed, nor is a second port to proxy to.
In all cases with the legacy setup, there is always a check of some kind that decides which requests will get sent to the servlet. This is usually checking for URLs ending in .cfm
or .cfc
. Common implementations of this are:
Adobe's CF connector
The mod_cfml module for Apache
BonCode for IIS
A custom proxy directive in your web server.
As of CommandBox 6, we have the same setting and it can be set as web.servletPassPredicate
which is simply an Undertow Predicate. By default, CommandBox uses the following predicate for the servletPassPredicate
:
So the URI /index.cfm
will get sent to the servlet, but /test.txt
would just get sent by the static file resource handler. You can customize this if necessary with any valid Undertow predicate. It can even be different on a per-site basis by setting it inside the sites
object or .site.json
file for a given site.
Note, be careful as your servlet pass setting may send .cfm
files to the static file handler. CommandBox has a failsafe in place to prevent the source code from being returned, but you should still use caution when configuring this.
Learn how (and where) to configure multiple sites from a single CommandBox server
As soon as CommandBox flips over to Multi-Site mode, the settings in the web
object will become defaults that apply to all sites. This allows you to group global settings into the top-level web
object and then override what you need for each site. Here is the full order of precedence for what settings will be applied:
settings in a .site.json
file inside a web root of a site
settings in an external site JSON file pointed to by the siteConfigFiles
setting in server.json
site-specific object in the sites
object of server.json
settings in the web
object of server.json
server.default
settings in CommandBox's global config settings
Since all sites for a given server do run inside the same JVM, there are some settings which cannot be customized on a per-site basis. They are as follows:
JRE/JDK the server runs on
JVM args, heap size
CF Engine/version
Console log
Tuckey Rewrites (part of servlet)
Environment Variables
Tray icon (there is a single tray icon for the entire server)
Everything normally set in the web
block of your server.json
can be configured separately for each site. These settings include:
GZIp enabled and GZip predicate
Access log
Use proxy forwarded IP
CommandBox Server Rules (Undertow Predicate Language)
SSL settings (HSTS, SSL Redirect)
Block CF Admin
Block flash remoting
Block sensitive paths
Security
Basic auth
Client cert auth
Security predicate
Custom error pages (404, 500, etc)
Mime types
Welcome files
Allowed file extensions
Directory browsing
Aliases/Virtual dirs
File cache settings
Case sensitive paths
Web root
Server Profile (even though this is not inside the web
object in your server.json
, it can still be set in a sites
block to override for that site.)
And remember, all of the settings in the section above can be defaulted for all sites in the web
block at the top of your server.json
and then overridden in the sites.siteName
block or in a .site.json
file.
There is much-improved console output now coming from Runwar when the server starts up. Add --verbose
or --debug
to your server start
command and you'll see site debug output at the top of the server start in the interactive job output:
Furthermore, once the actual server process gets underway, with the --trace
flag you'll see additional console output like so:
A visual overview of CommandBox server deployments for both singular and Multi-Site configurations
This is what a typical ColdFusion deployment looks like. A separate web server sites in front, bound to ports 80 and 443 and proxies back to a Tomcat-based servlet:
For a CommandBox server, we replace the green box above with JBoss Undertow instead of Tomcat and we configure it on the fly with CommandBox. Here's a look under the covers at what the internal pieces of CommandBox look like for a single site server.
We have a set of HTTP/SSL/AJP listeners that feed through a single handler chain of configuration that funnel requests to the servlet deployment where the CF engine's libs are loaded. The default servlet serves static files via the singular resource manager configured to look in the web root.
Here is what a Multi-Site server looks like under the covers for Lucee Server (which works differently than Adobe). This is mostly the same regardless of whether you're using ModCFML or the new Multi-Site features.
Here the listeners funnel to a handful of different initial handler chains, which represent the different configuration between sites. The Lucee jars are loaded once and the Lucee Servlet Context exists once. Each site has a separate servlet deployment where a separate Lucee Web Context lives, along with their own CFML Servlet, default servlet, and resource manager configured for their separate web root.
In Multi-Site mode, static files are NOT served by the default servlet, but instead by an Undertow resource handler in the initial handler chain. The web.servletPassPredicate
controls what requests even reach the servlet. Static files are served without ever touching the servlet.
Here is what a Multi-Site server looks like under the covers for Adobe ColdFusion (which works differently than Lucee). This is the mostly the same regardless of whether you're using ModCFML or the new Multi-site features.
Like the Lucee example above, the listeners pass requests off to one of several initial handler chains, which represent different per-site configurations. However, we only have a single servlet deployment in this case and a single default servlet. The resource manager delegates out to sub-resource managers based on the site being accessed to switch out the web root as necessary.
In Multi-Site mode, static files are NOT served by the default servlet, but instead by an Undertow resource handler in the initial handler chain. The web.servletPassPredicate
controls what requests even reach the servlet. Static files are served without ever touching the servlet.