We are writing to inform you of an upcoming change that may affect how you access and use resources from our platform.
We will be enforcing CORS policy on all generated artifact URLs. This means that all resources hosted on our platform will require appropriate handling for cross-origin requests, or in other words, you cannot directly serving generated model files using the link Tripo's API returns to you.
This change is part of our commitment to improving security and preventing unauthorized access to resources across different origins. CORS (Cross-Origin Resource Sharing) is a mechanism that allows servers to specify which external domains can access resources.
For more information about what is CORS, you can refer to the MDN's guide.
Currently, we do not provide CORS headers for artifact URLs. As a result, if you are using or linking to these resources directly in a browser or from external domains, you may experience issues.
To ensure continued access to these assets, you will need to take one of the following actions:
Need practical code examples? If you're unsure how to start or find the process hard to implement, we’ve provided practical code snippets to help you get started. Please refer to the Appendix for more details.
Is my generated file lost?
No. You can always retrieve a new URL from the API at any time.
I don't return your URL directly, but I do some post-processing. Should I be concerned?
If you're not returning our URL directly to your users, you can skip this change. However, we recommend monitoring your usage, especially if you notice any error spikes or abnormal behavior in your system.
We understand that this change may require updates to your workflow. If you need any help setting up an edge proxy or have any questions about this transition, please reach out to our support team at support@tripo3d.ai.
We appreciate your cooperation as we work to enhance security on our platform! Stay safe and productive!
Best regards,
The Tripo Team
If you're using FastAPI (which is easy to port or translate to any other framework or language), here's how you can adjust your existing code to handle CORS and save the artifact locally:
Current Code:
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # API works
return resp.json()['url']
You can easily modify it to:
import httpx
import uuid
from fastapi.responses import FileResponse
from fastapi import HTTPException
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # same as above
# Add the code below
file_id = str(uuid.uuid4())
with httpx.Client() as client:
# Download the artifact
response = client.get(url)
# Check if the request was successful
if response.status_code == 200:
# Then resave it
with open(f"./downloaded/{file_id}.glb", "wb") as file:
file.write(response.content)
return file_id
@app.get("/artifact/{file_id}")
def download(file_id: str) -> FileResponse:
if os.path.exists(f"./downloaded/{file_id}.glb"):
return FileResponse(f"./downloaded/{file_id}.glb")
raise HTTPException(status_code=404, detail="Item not found")
After downloading and saving the file locally, you should serve your users with the new URL. For example, if your application is hosted at https://app.example.com
, the URL you should be serving will look like this:
https://app.example.com/artifact/<file_id>
This code snippet is just a demonstration of how to adjust your integration. However, there are a few important things you should consider:
If you're developing a Single Page Application (SPA) or a Backend for Frontend (BFF) server and prefer not to handle file downloads and storage directly on your server, you can use an edge proxy approach. This allows you to fetch the file from our server, apply the necessary CORS headers, and then serve it to your users.
Below is an example of how to implement this approach using Cloudflare Workers. This solution will handle the CORS issues by downloading the file and resending it under your domain.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
// Parse the request URL
const url = new URL(request.url);
// Get the target URL from the 'url' query parameter
const targetUrl = url.searchParams.get('url');
// If no URL is provided, return an error
if (!targetUrl) {
return new Response('Please provide a URL parameter', {
status: 400,
headers: {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
}
// Handle preflight OPTIONS request
if (request.method === 'OPTIONS') {
return handleCORS();
}
try {
// Fetch the file from the target URL
const response = await fetch(targetUrl);
// If the fetch failed, return the error
if (!response.ok) {
return new Response(`Failed to fetch from target URL: ${response.statusText}`, {
status: response.status,
headers: corsHeaders()
});
}
// Get the content type from the response or default to octet-stream
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
// Get the content disposition or create one from the URL
let contentDisposition = response.headers.get('Content-Disposition');
if (!contentDisposition) {
// Extract filename from the URL
const fileName = targetUrl.split('/').pop().split('?')[0] || 'file';
contentDisposition = `attachment; filename="${fileName}"`;
}
// Create a new response with CORS headers
const newResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: {
'Content-Type': contentType,
'Content-Disposition': contentDisposition,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Cache-Control': 'public, max-age=3600' // Cache for 1 hour
}
});
return newResponse;
} catch (error) {
return new Response(`Error fetching the file: ${error.message}`, {
status: 500,
headers: corsHeaders()
});
}
}
// Handle CORS preflight requests
function handleCORS() {
return new Response(null, {
status: 204, // No content
headers: corsHeaders()
});
}
// Create CORS headers object
function corsHeaders() {
return {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Max-Age': '86400' // 24 hours
};
}
Once your Cloudflare Worker is set up, you can integrate it into your SPA by using the following JavaScript code to fetch the file:
fetch('https://your-worker-url.workers.dev/?url=<target_url>')
.then(response => response.blob())
.then(blob => {
// Create a download link
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'downloaded-file.pdf';
document.body.appendChild(a);
a.click();
a.remove();
})
.catch(error => console.error('Error:', error));
This solution is perfect for SPAs or BFF servers that need to bypass CORS restrictions while downloading and serving files to end users. The file is fetched from the target URL, passed through the edge proxy, and served with the necessary CORS headers.
If you prefer to set up a simple CORS proxy, you can refer to this example provided by Cloudflare: Cloudflare Workers - CORS Header Proxy
This will allow you to handle cross-origin requests more easily, while still complying with CORS policies.