Craft 3 Multi-site with no "default" site I have setup a Craft 3 multi-site with the following configuration:



But what I really want is:



Additionally, I want to have a 'default' http://localhost:8888 so I can always redirect to /en or /es based on the browser language, but I don't want to add it as a site in the CMS through the admin panel, because I end up with a third unwanted version for each entry. 

Which is the way to go?
This can be done in 3 main steps.



1. Get the site subdirectories working

First just get the /en/ and /es/ subdirectory working:


Create en/ and es/ subfolders in your webroot
Each subfolder should have its own index.php file and .htaccess file (if using Apache).
Assuming you used the craftcms/craft project as a starting point, update the define('CRAFT_BASE_PATH'... line in each of the index.php files to:

define('CRAFT_BASE_PATH', dirname(__DIR__, 2));

Update your English site’s Base URL to include the /en/.


At this point, if you point your browser directly to /en/, the English site should work as expected.

2. Create a redirect script in the webroot’s index.php

Create an index.php file in your webroot (alongside the en/ and es/ subfolders). This script will be responsible for redirecting the request to the correct subfolder, based on the requested URI and language.

&lt;?php

// Bootstrap Craft -------------------------------------------------------------

// Set path constants
define('CRAFT_BASE_PATH', dirname(__DIR__));
define('CRAFT_VENDOR_PATH', CRAFT_BASE_PATH.'/vendor');

// Load Composer's autoloader
require_once CRAFT_VENDOR_PATH.'/autoload.php';

// Load dotenv?
if (file_exists(CRAFT_BASE_PATH.'/.env')) {
    (new Dotenv\Dotenv(CRAFT_BASE_PATH))-&gt;load();
}

// Load Craft (but don't run)
define('CRAFT_ENVIRONMENT', getenv('ENVIRONMENT') ?: 'production');
/** @var \craft\web\Application $app */
$app = require CRAFT_VENDOR_PATH.'/craftcms/cms/bootstrap/web.php';

// Custom Logic ----------------------------------------------------------------

// find a site language that matches the browser's requested language
$siteLanguages = $app-&gt;i18n-&gt;getSiteLocaleIds();
$language = $app-&gt;request-&gt;getPreferredLanguage($siteLanguages);

// find the first site that uses that language
foreach ($app-&gt;sites-&gt;getAllSites() as $site) {
    if ($site-&gt;language === $language) {
        // found it
        break;
    }
}

// figure out where to redirect this request now
$url = (function() use ($app, $site) {
    $uri = $app-&gt;request-&gt;getPathInfo();

    // see if we have an exact URI/language match
    if ($element = $app-&gt;elements-&gt;getElementByUri($uri, $site-&gt;id, true)) {
        return $element-&gt;getUrl();
    }

    // see if we have an exact URI match in any other site
    foreach ($app-&gt;sites-&gt;getAllSites() as $otherSite) {
        if ($otherSite !== $site) {
            if ($otherSiteElement = $app-&gt;elements-&gt;getElementByUri($uri, $otherSite-&gt;id, true)) {
                // found one! now see if the element is also available in the requested site
                if ($element = $otherSiteElement::find()-&gt;id($otherSiteElement-&gt;id)-&gt;siteId($site-&gt;id)-&gt;one()) {
                    return $element-&gt;getUrl();
                }
                // otherwise we'll just ignore the browser language
                return $otherSiteElement-&gt;getUrl();
            }
        }
    }

    // no element matches. we'll just redirect to the same URI in the requested site.
    return \craft\helpers\UrlHelper::siteUrl($uri, null, null, $site-&gt;id);
})();

// redirect to the URL
$app-&gt;response-&gt;redirect($url)-&gt;send();


With that in place, you should be able to start testing the redirects by pointing your browser to /index.php?p=some/uri.

3. Redirect nonlocalized requests to the webroot’s index.php

Finally, we want to get all requests that don’t start with en/ or es/ to be routed to an index.php file in your web root. If you’re using Apache, you can do that with mod_rewrite:

# Send requests missing en/ or es/ to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/(en|es) [NC]
RewriteRule (.+) index.php?p=$1 [QSA,L]


Now everything should work!