Custom Domain Setup
Learn how to use your own URL for your Prometora marketplace. This guide walks you through the steps of connecting your custom domain using Cloudflare.
Note: A custom domain is not required. Prometora provides a branded domain (prometora.com/s/your-store) that you can use as long as you want.
You connect your custom domain to your Prometora marketplace by adding Domain Name System (DNS) records through Cloudflare and creating a Cloudflare Worker to route traffic to your store.
Prometora does not host your domain or offer domain hosting services. You should use domain registrars like GoDaddy, Namecheap, or Google Domains to purchase your domain. Then, you'll transfer DNS management to Cloudflare (free) and configure it to point to your Prometora marketplace.
Here are the overall steps for adding your custom domain to your marketplace:
First, you need to transfer DNS management to Cloudflare. This is a one-time setup that gives you access to free SSL certificates and a global CDN.
Important for email users: If you use email with your domain (like info@example.com), make sure your MX records are preserved in Cloudflare. Set mail-related A records to DNS only (grey cloud), not Proxied.
Cloudflare Workers allow you to route your custom domain to your Prometora marketplace.
The Worker needs the routing logic to look up your store and proxy requests to Prometora's servers.
VERCEL_DEPLOYMENT_URLhttps://www.prometora.comImportant: Use the complete code below. The Worker needs to look up which store owns your domain and route requests accordingly.
/**
* Cloudflare Worker for Custom Domain Routing
*/
// Your Vercel deployment URL
const VERCEL_URL = typeof VERCEL_DEPLOYMENT_URL !== 'undefined' ? VERCEL_DEPLOYMENT_URL : 'https://www.prometora.com';
const API_BASE_URL = `${VERCEL_URL}/api`;
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
const hostname = url.hostname;
// Skip for workers.dev domain (use for testing)
if (hostname.endsWith('.workers.dev')) {
return new Response('Cloudflare Worker is running! This is used for custom domain routing.', {
headers: { 'content-type': 'text/plain' }
});
}
try {
// Check if this is a static asset request
const isStaticAsset = url.pathname.startsWith('/_next/') ||
url.pathname.startsWith('/static/') ||
url.pathname.startsWith('/favicon.ico') ||
url.pathname.startsWith('/images/') ||
url.pathname.match(/\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$/);
// Check if this is an API request
const isApiRequest = url.pathname.startsWith('/api/');
// For static assets or API requests, proxy directly WITHOUT rewriting path
if (isStaticAsset || isApiRequest) {
const newUrl = new URL(request.url);
newUrl.hostname = new URL(VERCEL_URL).hostname;
newUrl.protocol = 'https:';
// Keep original pathname for API routes and static assets
const headers = new Headers(request.headers);
// For API requests, add the custom domain header so middleware knows which store
if (isApiRequest) {
headers.set('X-Custom-Domain', hostname);
}
const modifiedRequest = new Request(newUrl.toString(), {
method: request.method,
headers: headers,
body: request.body,
redirect: 'follow'
});
return await fetch(modifiedRequest);
}
// Step 1: Look up which store owns this domain (only for non-API requests)
const storeSlug = await getStoreByDomain(hostname);
if (!storeSlug) {
return new Response('Store not found for this domain', {
status: 404,
headers: { 'content-type': 'text/plain' }
});
}
// Step 2: Rewrite URL to custom-domain path on Vercel (not /s/${storeSlug})
const newUrl = new URL(request.url);
newUrl.hostname = new URL(VERCEL_URL).hostname;
newUrl.protocol = 'https:';
// Use /custom-domain path instead of /s/${storeSlug}
if (url.pathname === '/') {
newUrl.pathname = `/custom-domain`;
} else {
newUrl.pathname = `/custom-domain${url.pathname}`;
}
// Step 3: Create modified request
const modifiedRequest = new Request(newUrl.toString(), {
method: request.method,
headers: request.headers,
body: request.body,
redirect: 'follow'
});
// Add custom headers
const headers = new Headers(modifiedRequest.headers);
headers.set('X-Custom-Domain', hostname);
headers.set('X-Store-Slug', storeSlug);
headers.set('X-Forwarded-Host', hostname);
const finalRequest = new Request(modifiedRequest, { headers });
// Step 4: Fetch and return response
const response = await fetch(finalRequest);
const modifiedResponse = new Response(response.body, response);
modifiedResponse.headers.set('X-Served-By', 'Cloudflare-Worker');
// Handle redirects
if (modifiedResponse.headers.has('location')) {
let location = modifiedResponse.headers.get('location');
if (location.includes(VERCEL_URL)) {
location = location.replace(VERCEL_URL, `https://${hostname}`);
location = location.replace(`/custom-domain`, '');
modifiedResponse.headers.set('location', location);
}
}
return modifiedResponse;
} catch (error) {
console.error('Worker error:', error);
return new Response(`Error: ${error.message}`, {
status: 500,
headers: { 'content-type': 'text/plain' }
});
}
}
async function getStoreByDomain(domain) {
try {
const response = await fetch(`${API_BASE_URL}/stores/lookup?domain=${encodeURIComponent(domain)}`, {
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
console.error('Store lookup failed:', response.status);
return null;
}
const data = await response.json();
return data.slug || null;
} catch (error) {
console.error('Error looking up store:', error);
return null;
}
}After configuring the Worker code, attach your custom domain to it. This automatically creates the necessary DNS routing.
www.example.comNote: When you add a custom domain to a Worker, Cloudflare automatically creates the necessary DNS records. You don't need to manually add CNAME records.
If you're using www.example.com, you should redirect the apex domain (example.com) to www for consistency.
A@ (represents apex domain)192.0.2.1 (placeholder IP)https://example.com/* to https://www.example.com/$1301 (permanent redirect)The final step is to verify your domain in your Prometora store settings.
Success! Your marketplace is now live at your custom domain with automatic SSL encryption. Visit https://www.example.com to see your store.
You can host your marketplace at a subdomain like shop.example.com, marketplace.example.com, or store.example.com.
The process is the same - just use your chosen subdomain (e.g., shop.example.com) when adding the custom domain to the Worker in Step 4.
Domain shows "Pending verification"?
Seeing "Cloudflare Worker is running!" message?
Email stopped working?
SSL certificate error?
If you encounter issues or have questions about connecting your custom domain, please contact our support team at rasmus@speedbuildmarketplace.com. Include a screenshot of your DNS settings if you're unsure about your configuration.