Download Control#
Use the dl parameter to control the download behavior of images. You can choose whether the browser displays or downloads the image.
Usage#
| Parameter | Behavior | Description |
|---|---|---|
| (no parameter) | inline | Display image in browser (default behavior) |
transform=dl | attachment | Download with original filename |
transform=dl:custom-name | attachment | Download with custom filename (extension automatically adjusted to match converted format) |
Examples#
Display in browser (default behavior)
https://cdn.snapkit.studio/org/project/image.jpg
Download with original filename
https://cdn.snapkit.studio/org/project/image.jpg?transform=dl
Download with custom filename
https://cdn.snapkit.studio/org/project/image.jpg?transform=dl:my-photo
The extension is automatically adjusted to match the converted format. For example, an image converted to WebP will be downloaded as my-photo.webp.
Combined with transformations
https://cdn.snapkit.studio/org/project/image.jpg?transform=w:800,format:webp,dl:optimized-image
This example resizes the image to 800px width, converts it to WebP format, and downloads it as optimized-image.webp.
Security & Standards Compliance#
- Path Traversal Prevention: Automatically removes
..and path separators - XSS Prevention: Filters dangerous characters (
<>:"|?*and control characters) - Filename Length Limit: Maximum 255 characters
- RFC 5987/6266 Compliance: Safely handles international characters (Korean, Japanese, etc.)
- UTF-8 Encoding: Ensures correct filename display across all browsers
JavaScript/TypeScript Example#
// Create download URL
const createDownloadUrl = (imageUrl: string, filename?: string) => {
const url = new URL(imageUrl);
const existingTransform = url.searchParams.get('transform') || '';
const dlParam = filename ? `dl:${filename}` : 'dl';
// Append to existing transform or create new
const newTransform = existingTransform
? `${existingTransform},${dlParam}`
: dlParam;
url.searchParams.set('transform', newTransform);
return url.toString();
};
// Usage examples
const imageUrl = 'https://cdn.snapkit.studio/org/project/image.jpg';
// Download with original filename
const downloadOriginal = createDownloadUrl(imageUrl);
// → https://cdn.snapkit.studio/org/project/image.jpg?transform=dl
// Download with custom filename
const downloadCustom = createDownloadUrl(imageUrl, 'my-photo');
// → https://cdn.snapkit.studio/org/project/image.jpg?transform=dl:my-photo
// Combined with transformations
const imageWithTransform = 'https://cdn.snapkit.studio/org/project/image.jpg?transform=w:800,format:webp';
const downloadTransformed = createDownloadUrl(imageWithTransform, 'optimized');
// → https://cdn.snapkit.studio/org/project/image.jpg?transform=w:800,format:webp,dl:optimizedDownload Methods Comparison: <a download> vs Server-Side Control#
There are two main ways to control image downloads:
- Client-side: HTML
<a download>attribute - Server-side:
Content-Dispositionheader (Snapkit'sdlparameter)
Each approach is needed for different situations, and sometimes they're used together.
Feature Comparison#
| Feature | <a download> | dl Parameter (Server-side) |
|---|---|---|
| Applies to | HTML link clicks only | All HTTP requests |
| CORS Restrictions | ❌ Blocked for cross-origin | ✅ Works cross-origin |
| JavaScript fetch | ❌ Not applicable | ✅ Works |
| Direct URL access | ❌ Displays in browser | ✅ Downloads |
| Mobile apps | ❌ Not available | ✅ Available |
| API requests | ❌ Not available | ✅ Available |
When <a download> is Appropriate#
Simple downloads from same domain
<!-- ✅ Works well for same-origin resources -->
<a href="/uploads/document.pdf" download="my-document.pdf">
Download Document
</a>Dynamic filename control on client
const handleDownload = (filename: string) => {
const link = document.createElement('a');
link.href = '/api/file/123';
link.download = `${filename}-${Date.now()}.pdf`;
link.click();
};When dl Parameter (Server-side) is Required#
1. CDN/Cross-origin Downloads (Required)
// ❌ Blocked by CORS with <a download>
<a href="https://cdn.snapkit.studio/org/image.jpg" download="my-photo.jpg">
Download (doesn't work - cross-origin)
</a>
// ✅ Server-side dl parameter required
<a href="https://cdn.snapkit.studio/org/image.jpg?transform=dl:my-photo">
Download (works correctly)
</a>2. JavaScript fetch/API Requests
// ✅ Handle downloads with fetch
const response = await fetch(
'https://cdn.snapkit.studio/org/image.jpg?transform=dl:photo'
);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.click();3. Direct URL Access Auto-download
When users enter URL directly in browser:
https://cdn.snapkit.studio/org/image.jpg?transform=dl
→ Automatically downloads instead of displaying in browser4. Display or Download Same Image
<!-- Display in browser -->
<img src="https://cdn.snapkit.studio/org/image.jpg" />
<!-- Download same image -->
<a href="https://cdn.snapkit.studio/org/image.jpg?transform=dl">
Download
</a>Recommendations#
When using Snapkit:
- Use
dlparameter since CDN is on a different domain <a download>can be used supplementarily, but won't work cross-origindlparameter is required for API integration or fetch usage
Example combining both approaches:
<!-- Server-side control + client-side filename hint -->
<a
href="https://cdn.snapkit.studio/org/image.jpg?transform=dl:photo"
download="my-custom-photo.jpg"
>
Download
</a>The server's Content-Disposition header takes precedence, with the download attribute acting as a fallback.
Next Steps#
- Best Practices - Learn optimization strategies
- Use Cases - See real-world examples
