Answers.Body: <p>This can be done in 3 main steps.</p>
  
  
  
  <p><strong>1. Get the site subdirectories working</strong></p>
  
  <p>First just get the <code>/en/</code> and <code>/es/</code> subdirectory working:</p>
  
  <ul>
  <li>Create <code>en/</code> and <code>es/</code> subfolders in your webroot</li>
  <li>Each subfolder should have its own <code>index.php</code> file and <code>.htaccess</code> file (if using Apache).</li>
  <li><p>Assuming you used the <a href="https://github.com/craftcms/craft" rel="noreferrer">craftcms/craft</a> project as a starting point, update the <code>define('CRAFT_BASE_PATH'...</code> line in each of the <code>index.php</code> files to:</p>
  
  <pre class="lang-php prettyprint-override"><code>define('CRAFT_BASE_PATH', dirname(__DIR__, 2));
  </code></pre></li>
  <li><p>Update your English site’s Base URL to include the <code>/en/</code>.</p></li>
  </ul>
  
  <p>At this point, if you point your browser directly to <code>/en/</code>, the English site should work as expected.</p>
  
  <p><strong>2. Create a redirect script in the webroot’s index.php</strong></p>
  
  <p>Create an <code>index.php</code> file in your webroot (alongside the <code>en/</code> and <code>es/</code> subfolders). This script will be responsible for redirecting the request to the correct subfolder, based on the requested URI and language.</p>
  
  <pre class="lang-php prettyprint-override"><code>&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();
  </code></pre>
  
  <p>With that in place, you should be able to start testing the redirects by pointing your browser to <code>/index.php?p=some/uri</code>.</p>
  
  <p><strong>3. Redirect nonlocalized requests to the webroot’s index.php</strong></p>
  
  <p>Finally, we want to get all requests that don’t start with <code>en/</code> or <code>es/</code> to be routed to an <code>index.php</code> file in your web root. If you’re using Apache, you can do that with <code>mod_rewrite</code>:</p>
  
  <pre class="lang-php prettyprint-override"><code># 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]
  </code></pre>
  
  <p>Now everything should work!</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-03-01T21:48:38.630Z
Answers.Text: Hi, I've tried this and get an error message: Fatal error: Uncaught TypeError: Argument 1 passed to Dotenv\Dotenv::__construct() must be an instance of Dotenv\Loader, string given. On line 14. Do you know why that could occur?
Answers.UserDisplayName: outline4
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-02-06T13:26:46.457Z
Answers.LastActivityDate: 2018-02-06T15:52:56.117Z
Answers.LastEditDate: 2018-02-06T15:52:56.117Z
Answers.UserDisplayName: Brandon Kelly
Body: <p>I have setup a Craft 3 multi-site with the following configuration:</p>
  
  <p><a href="https://i.sstatic.net/vR8NL.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/vR8NL.png" alt="enter image description here"></a></p>
  
  <p>But what I really want is:</p>
  
  <p><a href="https://i.sstatic.net/A3L1R.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/A3L1R.png" alt="enter image description here"></a></p>
  
  <p>Additionally, I want to have a 'default' <strong><a href="http://localhost:8888" rel="nofollow noreferrer">http://localhost:8888</a></strong> so I can always <strong>redirect</strong> 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 <strong>for each entry</strong>. </p>
  
  <p>Which is the way to go?</p>
  
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2018-02-05T20:08:06.423Z
Comments.Text: Going with your 2nd image, you could just drop an `.htaccess` or `index.php` into whatever folder `http://localhost:8888` is configured to serve with your redirect logic in it, no?
Comments.UserDisplayName: Brad Bell
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2018-02-05T21:08:09.513Z
Comments.Text: @BradBell I have already unsuccessfully tried that but I may be doing something I shouldn't. My goal is to map my current `mysite.com` and all it's sections into `mysite.com/en` and `mysite.com/es`. Just like microsoft.com is doing; you can never browse `microsoft.com/section` but instead you get redirected to `microsoft.com/en-US/section`.
  I got this to work only when I create a third Craft site: 
  - default (.htaccess and index.php are under public/)
  - English (.htaccess and index.php are under public/en)
  - Spanish (they are under public/es)
Comments.UserDisplayName: Rosana Ruiz
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2018-02-05T21:09:08.947Z
Comments.Text: I want to be able to request mySite.com => Then I check craft.request and, based on browser language and other things, decide where I redirect. (I have this part working)
Comments.UserDisplayName: Rosana Ruiz
Tags: <localization><craft3><multi-site>
Title: Craft 3 Multi-site with no "default" site
ThreadText: # Craft 3 Multi-site with no "default" site
  
  (Asked by: username_0 on 2018-02-05)
  
  I have setup a Craft 3 multi-site with the following configuration:
  
  [![enter image description here](https://i.sstatic.net/vR8NL.png)](https://i.sstatic.net/vR8NL.png)
  
  But what I really want is:
  
  [![enter image description here](https://i.sstatic.net/A3L1R.png)](https://i.sstatic.net/A3L1R.png)
  
  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?
  
  **Tags:** localization, craft3, multi-site
  
  > **Comments:**
  > **username_1**: Going with your 2nd image, you could just drop an `.htaccess` or `index.php` into whatever folder `http://localhost:8888` is configured to serve with your redirect logic in it, no?  
  > **username_0**: @username_1 I have already unsuccessfully tried that but I may be doing something I shouldn't. My goal is to map my current `mysite.com` and all it's sections into `mysite.com/en` and `mysite.com/es`. Just like microsoft.com is doing; you can never browse `microsoft.com/section` but instead you get redirected to `microsoft.com/en-US/section`. I got this to work only when I create a third Craft site:  - default (.htaccess and index.php are under public/) - English (.htaccess and index.php are under public/en) - Spanish (they are under public/es)  
  > **username_0**: I want to be able to request mySite.com => Then I check craft.request and, based on browser language and other things, decide where I redirect. (I have this part working)  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_2 on 2018-02-06. Score: 6 | Accepted answer
  
  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](https://github.com/craftcms/craft) project as a starting point, update the `define('CRAFT_BASE_PATH'...` line in each of the `index.php` files to:
  
    ```php
    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.
  
  ```php
  <?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))->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->i18n->getSiteLocaleIds();
  $language = $app->request->getPreferredLanguage($siteLanguages);
  
  // find the first site that uses that language
  foreach ($app->sites->getAllSites() as $site) {
      if ($site->language === $language) {
          // found it
          break;
      }
  }
  
  // figure out where to redirect this request now
  $url = (function() use ($app, $site) {
      $uri = $app->request->getPathInfo();
  
      // see if we have an exact URI/language match
      if ($element = $app->elements->getElementByUri($uri, $site->id, true)) {
          return $element->getUrl();
      }
  
      // see if we have an exact URI match in any other site
      foreach ($app->sites->getAllSites() as $otherSite) {
          if ($otherSite !== $site) {
              if ($otherSiteElement = $app->elements->getElementByUri($uri, $otherSite->id, true)) {
                  // found one! now see if the element is also available in the requested site
                  if ($element = $otherSiteElement::find()->id($otherSiteElement->id)->siteId($site->id)->one()) {
                      return $element->getUrl();
                  }
                  // otherwise we'll just ignore the browser language
                  return $otherSiteElement->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->id);
  })();
  
  // redirect to the URL
  $app->response->redirect($url)->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`:
  
  ```php
  # 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!
  
  > **Comments:**
  > **username_3**: Hi, I've tried this and get an error message: Fatal error: Uncaught TypeError: Argument 1 passed to Dotenv\Dotenv::__construct() must be an instance of Dotenv\Loader, string given. On line 14. Do you know why that could occur?  
  
  ---
  
  