# Source: https://www.daytona.io/docs/en/preview.md

The Daytona SDK provides methods to generate preview links for accessing Sandboxes. Any process listening for HTTP traffic on ports 3000–9999 can be previewed.

Daytona offers two approaches for preview URLs, each with different authentication mechanisms: **Standard Preview URL** and **Signed Preview URL**.

## Authentication

If the Sandbox has its `public` property set to `true`, preview links will be publicly accessible without authentication. Otherwise, authentication is required.

For private sandboxes, the authentication differs based on the preview URL type.

## Standard Preview URL

The standard preview URL uses your sandbox ID in the URL structure and requires a separate authentication token sent via header.

**URL Structure:** `https://{port}-{sandboxId}.{daytonaProxyDomain}`

**How it works:**
- The URL contains the port and your sandbox ID
- A separate authentication token is provided
- The token must be sent in the `x-daytona-preview-token` request header
- The token automatically resets when the sandbox restarts

**When to use:**
- Programmatic access and API integrations where you control the HTTP headers
- Internal tools where token management through headers is straightforward

```python
preview_info = sandbox.get_preview_link(3000)

print(f"URL: {preview_info.url}")
print(f"Token: {preview_info.token}")

# Use with requests
import requests
response = requests.get(
    preview_info.url,
    headers={"x-daytona-preview-token": preview_info.token}
)
```

```typescript
const previewInfo = await sandbox.getPreviewLink(3000);

console.log(`URL: ${previewInfo.url}`);
console.log(`Token: ${previewInfo.token}`);

// Use with fetch
const response = await fetch(previewInfo.url, {
  headers: { 'x-daytona-preview-token': previewInfo.token }
});
```

```ruby

preview_info = sandbox.preview_url(3000)

puts "Preview link url: #{preview_info.url}"
puts "Preview link token: #{preview_info.token}"

```


See: [get_preview_link (Python SDK)](https://www.daytona.io/docs/python-sdk/sync/sandbox.md#sandboxget_preview_link), [getPreviewLink (TypeScript SDK)](https://www.daytona.io/docs/typescript-sdk/sandbox.md#getpreviewlink), [preview_url (Ruby SDK)](https://www.daytona.io/docs/ruby-sdk/sandbox.md#preview_url)

### Authentication

The URL uses the sandbox ID, and authentication is done via header:

```bash
# URL structure: port-sandboxid.domain
curl -H "x-daytona-preview-token: vg5c0ylmcimr8b_v1ne0u6mdnvit6gc0" \
  https://3000-sandbox-123456.proxy.daytona.work
```

## Signed Preview URL

The signed preview URL embeds the authentication token directly into the URL structure itself, eliminating the need for separate authentication headers.

**URL Structure:** `https://{port}-{token}.{daytonaProxyDomain}`

**How it works:**
- The URL contains the port and the signed authentication token (instead of the sandbox ID)
- Authentication is embedded in the URL - no headers needed
- You set a custom expiry time for the token (defaults to 60 seconds)
- The token persists across sandbox restarts until it expires
- You can manually revoke tokens before they expire if needed

**When to use:**
- Sharing preview links with users who can't easily set custom headers
- Embedding in iframes, emails, or other contexts where header control is limited
- Creating time-limited shareable links with explicit expiration
- Webhooks and integrations that don't support custom headers

```python
# Create a signed preview URL that expires in 3600 seconds (1 hour)
signed_url = sandbox.create_signed_preview_url(3000, expires_in_seconds=3600)

print(f"URL: {signed_url.url}")  # Token is embedded in the URL
print(f"Token: {signed_url.token}")  # Can be used to revoke access

# Use directly - no headers needed
import requests
response = requests.get(signed_url.url)

# Revoke the token before expiry if needed
sandbox.expire_signed_preview_url(3000, signed_url.token)
```

```typescript
// Create a signed preview URL that expires in 3600 seconds (1 hour)
const signedUrl = await sandbox.getSignedPreviewUrl(3000, 3600);

console.log(`URL: ${signedUrl.url}`);  // Token is embedded in the URL
console.log(`Token: ${signedUrl.token}`);  // Can be used to revoke access

// Use directly - no headers needed
const response = await fetch(signedUrl.url);

// Revoke the token before expiry if needed
await sandbox.expireSignedPreviewUrl(3000, signedUrl.token);
```

```ruby
# Create a signed preview URL that expires in 3600 seconds (1 hour)
signed_url = sandbox.create_signed_preview_url(3000, expires_in_seconds=3600)

puts "URL: #{signed_url.url}"
puts "Token: #{signed_url.token}"
```

See: [create_signed_preview_url (Python SDK)](https://www.daytona.io/docs/python-sdk/sync/sandbox.md#sandboxcreate_signed_preview_url), [getSignedPreviewUrl (TypeScript SDK)](https://www.daytona.io/docs/typescript-sdk/sandbox.md#getsignedpreviewurl), [preview_url (Ruby SDK)](https://www.daytona.io/docs/ruby-sdk/sandbox.md#preview_url)

### Authentication

The URL embeds the token in place of the sandbox ID - no headers needed:

```bash
# URL structure: port-token.domain (token is in the URL itself)
curl https://3000-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.proxy.daytona.work
```

:::tip
If you want to serve the previews under your own domain instead of using Daytona's URLs, check out the [Custom Domain/Authentication](https://www.daytona.io/docs/en/custom-domain-authentication.md) section.
:::

## Warning Page

When opening the preview link in a browser, a warning page will be shown for the first time.
This warning serves as a security measure to inform users about the potential risks of visiting the preview URL.

The warning page will only be shown when loading the preview link in a browser.

To avoid this warning you can do one of the following:

- Send the `X-Daytona-Skip-Preview-Warning: true` header
- Upgrade to [Tier 3](https://www.daytona.io/docs/en/limits.md)
- Deploy your own [custom preview proxy](https://www.daytona.io/docs/en/custom-domain-authentication.md)