> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Share subdomains across different projects

> Learn how to host subdomains from the same apex domain across multiple Upsun projects using a DNS TXT record and DNS challenge.

export const VariableBlock = ({name}) => {
  return <var spellCheck={false} title={`Replace '${name}' with your own data`}>{name}</var>;
};

By default, Upsun lets only one project at a time use a given domain. This prevents other users
from claiming your subdomains. If you need multiple Upsun projects to share subdomains of the same root domain
(the *apex domain*, for example `example.com`), you must explicitly allow this by adding a DNS TXT record and optionally, a DNS challenge.

Sharing subdomains across projects is common in the following scenarios:

* **Decoupled architectures**: A frontend project (for example, Next.js or React) uses `example.com` and a separate
  backend API project uses `api.example.com`, with independent resource allocations per project.
* **Microservices**: A product is split into independently deployable services, each mapped to its own subdomain
  (`checkout.example.com`, `account.example.com`, `catalog.example.com`). Sharing an apex domain can simplify cookie
  and session handling and reduce cross-origin complexity, but requests between subdomains are still cross-origin and may require cross-origin
  reference sharing (CORS).
* **Multi-tenant applications**: Each customer gets a dedicated subdomain (`client-a.example.com`,
  `client-b.example.com`) with private data and configuration, while all subdomains share the same apex domain.
* **Multi-region deployment**: Identical codebases run in different Upsun projects in different
  geographic regions. Users reach the project in their region through the same parent domain.

To host multiple subdomains within a *single* project instead, use [routes](/docs/routes).

<Info>
  <h4>Domain ownership verification</h4>
  The steps below protect against subdomain takeover within Upsun. To also prevent other organizations
  from claiming your domain, complete the recommended [DNS challenge](/docs/domains/steps/dns-challenge) in step 4.
</Info>

If you attempt to add a subdomain that is already in use by another project,
you get an error similar to the following:

```text {no-copy="true"} theme={null}
This domain is already claimed by another service
```

## Enable subdomains across multiple projects

<Info>
  <h4>Two different TXT records, two different purposes</h4>

  This guide refers to two separate DNS `TXT` records that serve distinct roles:

  | Record                                            | Purpose                                                     | Lifetime                                               |
  | ------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------ |
  | `_public-suffix-root.<YOUR_APEX_DOMAIN>` (step 1) | Tells Upsun to allow multiple projects to share your domain | **Temporary** — remove it after linking all subdomains |
  | `_upsun-organization.<YOUR_DOMAIN>` (step 4)      | Proves your organization owns the domain                    | **Permanent** — keep it in place                       |

  `<YOUR_DOMAIN>` in the DNS challenge record can be your apex domain (`example.com`) or any subdomain level (`app.example.com`). A record set at a parent level covers all subdomains beneath it — you don't need a separate record for each subdomain.

  The first record enables subdomain sharing within Upsun. The second prevents a different organization from claiming your domain entirely. They solve different problems.
</Info>

1. Add a `TXT` DNS record for your apex domain. This tells Upsun to allow multiple projects to use
   different subdomains under the same root.

   ```text {no-copy="true"} theme={null}
   _public-suffix-root.<YOUR_APEX_DOMAIN> TXT "public-suffix-root=<YOUR_APEX_DOMAIN>"
   ```

   Replace `<YOUR_APEX_DOMAIN>` with your domain (for example, `example.com`). This temporarily tells Upsun
   to treat your domain like a top-level domain — which allows each subdomain to be assigned to a different project.
   See [how domain reservation works](#how-domain-reservation-works) for a full explanation.

2. Add each subdomain to its respective Upsun project. Follow the standard [domain setup steps](/docs/domains/steps)
   for each project.

   <Info>
     <h4>Domain already claimed?</h4>
     If your domain is locked to a specific project (for example, if Upsun support added it manually), neither
     the apex domain (`example.com`)
     nor any subdomain (`app.example.com`) can be added to a second project. [Contact Support](/docs/core-concepts/get-support)
     and include the project ID of the project that already has the domain.
   </Info>

3. Remove the `TXT` record. This reinstates [subdomain takeover protection](#how-domain-reservation-works) and prevents other
   users from adding your subdomains to their own projects.

   If you don't remove the `TXT` record:

   * Other users can claim subdomains of your domain unless you have wildcard DNS records pointing at Upsun.
     Wildcard DNS records are the one thing that blocks other users from claiming subdomains.
   * You can't add your apex domain to a different project until you remove the record.

4. (Recommended, but not required) Set up a [DNS challenge](/docs/domains/steps/dns-challenge) to verify organizational ownership
   of your domain. This uses a **separate** `TXT` record (`_upsun-organization.<YOUR_DOMAIN>`) that is different from the one in
   step 1, and unlike the step 1 record, you keep it permanently. It prevents a different organization on Upsun
   from claiming your domain, even if your DNS still points to Upsun's infrastructure.

## How domain reservation works

Upsun's domain sharing behaviour is built on two security concepts from the browser world. Expand either for a full explanation.

<AccordionGroup>
  <Accordion title="The Public Suffix List">
    Upsun's subdomain sharing feature is modeled on the browser Public Suffix List (PSL) — a standard that
    determines which domains get treated like top-level domains for security purposes.

    Domain names are divided into hierarchical levels separated by `.`. The rightmost part, such as `.com`, `.edu`, or `.fr`,
    is the top-level domain (TLD). Browsers apply special rules to TLDs. For example, a page at `app.region.checkout.example.com` can
    set a cookie scoped to `example.com`, making it available to all its subdomains. That same page can't set a cookie scoped
    to `.com` itself, since that would affect every `.com` site.

    Browser vendors extend these rules through the [Public Suffix List (PSL)](https://publicsuffix.org/), a curated list of
    domain suffixes that receive TLD-like treatment. If `example.com` were on the PSL, browsers would reject a cookie scoped
    to `example.com` when set by a page at `app.example.com`. They would still accept a cookie set directly by `example.com`.
  </Accordion>

  <Accordion title="Subdomain takeover protection">
    By default, when you add a domain to an Upsun project, Upsun reserves the first level of
    that domain not covered by the PSL. Adding `app.region.checkout.example.com` reserves `example.com` for that project, so no other
    project can claim any domain under `*.example.com`. You can still add multiple subdomains within that same project.

    This prevents subdomain takeover. Without this protection, a malicious user could register `evil.example.com` in a
    separate project and use it to set cookies on your `example.com` site.

    This is secure by default, but it creates a conflict when you legitimately want multiple separate projects to share
    subdomains of the same apex domain.

    Upsun solves this with a small extension to the PSL. When you add a `TXT` record for a domain,
    Upsun treats that domain as a PSL entry. This shifts what gets reserved when you add a subdomain:

    * Without the `TXT` record: adding `app.region.checkout.example.com` reserves `example.com` for that project.
    * With the `TXT` record for `example.com`: adding `app.region.checkout.example.com` reserves only `checkout.example.com`.
      Another project can then claim `api.example.com` without conflict.

    You can apply this at any subdomain level. If you set a `TXT` record for `checkout.example.com` and add
    `app.region.checkout.example.com` to a project, only `region.checkout.example.com` is reserved for that project.
  </Accordion>
</AccordionGroup>
