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

# Serve Blob Storage Files from Your Own Domain

> Serve public Blob Storage files from a subdomain you own, like cdn.example.com, with a CNAME to cname.vertracloud.dev. Enterprise plans only.

On Enterprise plans, public files can be served from your own subdomain, for example `cdn.example.com/images/logo.png`, with HTTPS issued and renewed for you.

| Plan | Domains |
| - | - |
| Economy to Intermediary | 0 |
| Enterprise-4, 6 and 8 | 1 |
| Enterprise-12 to 24 | 3 |
| Enterprise-26 to 32 | 10 |

Without the right plan the call answers `PLAN_RESTRICTED_FEATURE`. `BLOB_CUSTOM_DOMAIN_LIMIT` is only for a plan that has domains and has used them all.

<Steps>
  <Step title="Add the hostname">
    Use a subdomain only; a bare root domain is not accepted. Optionally set a `prefix` so the domain serves a single folder.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X POST "https://blob.vertracloud.app/v1/domains" \
        -H "Authorization: Bearer $VERTRA_API_KEY" -H "Content-Type: application/json" \
        -d '{ "hostname": "cdn.example.com", "prefix": "images/" }'
      ```

      ```javascript JavaScript theme={null}
      const domain = await blob.domains.add("cdn.example.com");
      ```

      ```python Python theme={null}
      domain = client.blob.domains.add("cdn.example.com")
      ```

      ```go Go theme={null}
      domain, err := client.Blob.Domains.Add(ctx, "cdn.example.com", "", false)
      ```
    </CodeGroup>
  </Step>

  <Step title="Create the CNAME record">
    At your DNS provider:

    | Type | Name | Value |
    | - | - | - |
    | `CNAME` | `cdn` | `cname.vertracloud.dev` |
  </Step>

  <Step title="Create the ownership TXT record">
    While the domain is `pending`, the response carries a `verification` object with the TXT record that proves the hostname is yours. Create it next to the CNAME:

    | Type | Name | Value |
    | - | - | - |
    | `TXT` | `verification.name` | `verification.value` |

    The domain never becomes `active` on the CNAME alone. Once it is `active`, `verification` is no longer returned.
  </Step>

  <Step title="Verify">
    The domain starts as `pending`. When the CNAME and the ownership TXT record both resolve and the certificate is issued it becomes `active`. Force a new check with [Verify domain](/api-reference/endpoint/blob/domains/verify); `ssl_status` shows certificate progress. While the CNAME or the TXT record can't be found, the domain stays `pending`. A `failed` status means the hostname or its certificate could not be set up. DNS changes can take a few minutes to propagate. You can read `verification` again at any time from [List](/api-reference/endpoint/blob/domains/list).
  </Step>
</Steps>

## Serve web pages

By default a custom domain only serves files for download: `.html`, `.htm`, `.xhtml`, `.svg` and `.xml` always download, the same as on the default public link. Turn on `web_content` to serve those types in the browser with their real content type, for example to host a static page or show an SVG inline.

Pages served this way run JavaScript in a sandbox: no forms, no cookies and no navigating the top tab. Even so, you are publishing content that visitors open in their browser, so turn it on only for domains whose files you control, and use a `prefix` to limit it to one folder. The default public link never serves these types in the browser, whatever the setting.

<Steps>
  <Step title="In the dashboard">
    Open Blob Storage, go to Custom domains, and switch on web content for the domain. You can also set it when you add the domain.
  </Step>

  <Step title="With the API">
    Set it when adding the domain, or change it later with [Update domain](/api-reference/endpoint/blob/domains/update).

    ```bash cURL theme={null}
    curl -X PATCH "https://blob.vertracloud.app/v1/domains/$DOMAIN_ID" \
      -H "Authorization: Bearer $VERTRA_API_KEY" -H "Content-Type: application/json" \
      -d '{ "web_content": true }'
    ```

    ```javascript JavaScript theme={null}
    await blob.domains.update(domain.id, { web_content: true });
    ```

    ```python Python theme={null}
    client.blob.domains.update(domain["id"], web_content=True)
    ```

    ```go Go theme={null}
    domain, err := client.Blob.Domains.Update(ctx, domain.ID, true)
    ```
  </Step>
</Steps>

The change takes effect within about a minute.

A hostname already used elsewhere answers `BLOB_CUSTOM_DOMAIN_TAKEN`. Invalid hostnames answer `INVALID_CUSTOM_DOMAIN_FORMAT`. Remove a domain with [Delete domain](/api-reference/endpoint/blob/domains/delete); see also [List](/api-reference/endpoint/blob/domains/list) and [Add](/api-reference/endpoint/blob/domains/add).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.