Custom Domain Mapping with WordPress Multisite and Auto-SSL

Custom Domain Mapping with WordPress Multisite is one of the challenges part of making the WaaS/SaaS platform. And it can get even more complicated when you need to include SSL certificates for each site.

In this post, we will cover the mechanics of domain mapping and auto SSL. (This is the second part of our series on Building a WaaS Platform with WordPress Multisite, if you haven’t read the previous post, you might want to read it first.)

Custom Domain (aka Domain Mapping)

Custom domain, also known as domain mapping, is the process of using your own domain name instead of the default domain name provided by your website hosting service. For example, we give a random address to host your website, your website URL will be something like “”. However, with a custom domain, you can use your own domain name such as “”.

Custom Domain Mapping is a native WordPress feature since version 4.5+. Prior to WordPress 4.5, domain mapping necessitated the utilization of a domain mapping plugin such as WordPress MU Domain Mapping.

Custom Domain Mapping Screen on Multisite Settings Screen

If your WordPress multisite is the default application of the server, you can basically route your domain address (either A record or CNAME) to the server right after updating siteurls.

Update wp-config.php


or, you can use something like this: (we don’t share cookies across sites, in order to allow unfiltered_html capability to site admins)

define('COOKIE_DOMAIN', '');

Caddyserver to manage all SSL/TLS certificates

Caddyserver is an amazing web server that includes automatic SSL/TLS certificate management. It is designed to be easy to use, secure, and flexible. Caddyserver uses Let’s Encrypt (and ZeroSSL as a fallback) to automatically generate and renew SSL/TLS certificates for your domain names. This means that you do not have to manually obtain and install certificates.

And the best part is that Caddyserver has “On-Demand TLS” support, which makes Caddy an ideal choice for WordPress multisite.

Just for comparison, we have mentioned WP Ultimo in the previous post. WP Ultimo takes care of SSL certificates by using various hosting APIs and integrations, but it is quite limited when compared with the “on-demand TLS” approach. For instance, Let’s encrypt doesn’t allow more than 100 hostnames into a single certificate. And, if you are adding a new domain as an alias, your server needs to reload the configuration. If the user hasn’t configured the DNS beforehand, the authorization will fail, which could cause certificates to not load correctly.

Now, let’s dig into the more technical part;

Our server schema looks like this. We use hetzner cloud for the servers and floating IP ( for the caddyserver. Internal communication is done in private network.

Caddyserver sits in front of the application as a reverse proxy, and all traffic passes through Caddy as a load balancer.

Caddyserver On-Demand TLS Configuration for WordPress Multisite

Let’s start configuration with our Caddyfile

        on_demand_tls {
                interval 2m
                burst 5

With this configuration, we check the API endpoint to verify whether the domain is registered on our multisite or not. And then we add throttle to certificate requests to 5 every 2 minutes.


Now, we need to create domain-verify endpoint to check requested domain registered.

add_action( 'rest_api_init', __NAMESPACE__ . '\\register_routes' );

Register rest route:

function register_routes() {

				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => __NAMESPACE__ . '\\domain_verification_endpoint',
				'permission_callback' => '__return_true',
				'args'                => [
					'domain' => [
						'type'     => 'string',
						'required' => true,


And the callback:

function domain_verification_endpoint( $request ) {
	global $wpdb;

	$params        = $request->get_params();
	$ip            = $_SERVER['REMOTE_ADDR'];
	$proxy_ip_list = defined( 'TRUSTED_PROXY_IPS' ) ? TRUSTED_PROXY_IPS : [];

	// Check if the IP is in the proxy list for prod env
	if ( ! in_array( $ip, $proxy_ip_list, true ) && 'production' === wp_get_environment_type() ) {
		return new \WP_Error( 'waas_api_error', 'IP not in proxy list', [ 'status' => 403 ] );


	// Check if the domain is set. Ask endpoint will send the domain.
	if ( empty( $params['domain'] ) ) {
		return new \WP_Error( 'waas_api_error', 'Domain not set', [ 'status' => 403 ] );


	$domain = esc_url( $params['domain'] );
	$domain = wp_parse_url( $domain, PHP_URL_HOST );

	// Grab both WWW and no-WWW
	if ( strpos( $domain, 'www.' ) === 0 ) {
		$www   = $domain;
		$nowww = substr( $domain, 4 );
	} else {
		$nowww = $domain;
		$www   = 'www.' . $domain;

	$domains = [ $www, $nowww ];

	$domains_in = "'" . implode( "','", $domains ) . "'";

	$sites = $wpdb->get_results( "SELECT * FROM $wpdb->blogs where domain in({$domains_in})" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared

	if ( ! empty( $sites ) ) {
		wp_send_json_success( 'VALID DOMAIN', 200 );

	return new \WP_Error( 'waas_api_error', 'Invalid domain', [ 'status' => 405 ] );


For the extra protection of the endpoint, it will only respond to trusted IP addresses, which can be defined in wp-config.php

Once the configuration is done, your Caddy server will send a request like this: and it will issue the certificate if get the successful response.

Caddyserver Configuration

Initially, our goal is to ensure that all domains automatically operate on HTTPS, which is a secure protocol, rather than using unencrypted HTTP.

http:// {
        redir https://{host}{uri}

This will redirect when trying to reach to URL by http://

Wildcard SSL Certificate

First, we need to configure Caddyserver for wildcard SSL. If you are using Cloudflare, you can use Cloudflare module for Caddy. (Let’s encrypt requires ACME DNS challenge when issuing wildcard SSL.)

* {

        tls {
                dns cloudflare CLOUDFLARE_API_TOKEN

        reverse_proxy {
                transport http {


On-Demand TLS for Mapped Domains

https:// {
        tls {
        reverse_proxy {
                header_up clientip {remote_host}
                header_up HTTPS "on"      


You may want to read this guide as well:

Admin Interface

At NoCodeWP, we have a custom-made interface to update the domain. Once you set the domain, an automatic search/replace job runs in the background, and your website will be working with the domain you chose right after updating the DNS records.

As Caddyserver takes care of all the SSL-related problems, it should work seamlessly with all domain mapping plugins, such as Mercator and WP Ultimo, without additional setup. You have the flexibility to customize the endpoint as you desire.

In conclusion, setting up custom domain mapping with WordPress Multisite and Auto-SSL can seem like a daunting task, but it is a worthwhile endeavor for anyone who wants to build the next WaaS/SaaS platform top on WordPress multisite.

By following the steps outlined in this post, you can ensure that your custom domains are properly mapped, and SSL/TLS certificates are automatically provisioned and renewed. With this setup, you can focus on your platform without worrying about technical details related to SSL/TLS certificates and domain mapping. We hope this tutorial has been helpful.

If you have any questions or feedback, please let us know in the comments below. And, if you need maintenance & support for your multisite, you can check our plans.

Leave a Comment

Get peace of mind! We take care of hosting, support, and maintenance for your WordPress.