Domain validation is the mechanism that ties a software license to a specific website. For SaaS embeds, WordPress plugins, JavaScript libraries, and API integrations, domain validation is the most reliable method of preventing unauthorized use. But the implementation details make or break the system.
How Domain Validation Works
- Extraction — Your software extracts the current hostname from the runtime environment
- Transmission — The hostname and license key are sent to your validation API
- Matching — Your server checks if the domain is authorized for this key
- Response — Valid or invalid, with details about the license status
// Client-side domain extraction
const currentDomain = window.location.hostname // "app.example.com"
// Server-side validation
const isValid = await validateLicense({
key: customerKey,
domain: currentDomain
})
// Returns: { valid: true, licensedTo: "example.com", plan: "professional" }
Domain Matching Strategies
Exact Match
The simplest approach: the domain must match exactly. example.com only matches example.com — not www.example.com or app.example.com.
When to use: When you want strict control over exactly which domain uses the license.
Root Domain Match
Normalize all subdomains to the root domain. app.example.com, www.example.com, and example.com all match a license for example.com.
// Root domain extraction
const getRootDomain = (hostname) => {
const parts = hostname.split('.')
// Handle 2-part TLDs like .co.uk, .com.au
const twoPartTLDs = ['co.uk', 'com.au', 'co.nz', 'com.br', 'co.jp']
const suffix = parts.slice(-2).join('.')
if (twoPartTLDs.includes(suffix)) return parts.slice(-3).join('.')
return parts.slice(-2).join('.')
}
getRootDomain('app.staging.example.com') // "example.com"
getRootDomain('shop.example.co.uk') // "example.co.uk"
When to use: For most SaaS licensing. This is the most customer-friendly approach — they register one domain and all subdomains work.
Wildcard Pattern Match
Allow wildcard patterns like *.example.com for customers who need specific subdomain control. Useful for multi-tenant platforms where each tenant has their own subdomain.
Handling Edge Cases
www vs non-www
Always normalize. www.example.com and example.com should be treated as the same domain. Strip the www. prefix during validation.
Localhost and Development
Don't force developers to burn a domain slot for local development. Whitelist these development domains globally:
localhostand127.0.0.1*.localand*.test*.localhost- Common dev tools:
*.ngrok.io,*.vercel.app(configurable)
Staging Environments
Production licenses should work on staging by default. Match staging.example.com against a license for example.com using root domain matching.
IP Addresses
Some customers access applications by IP address during development or on internal networks. Support IP-based validation alongside domain matching.
Internationalized Domain Names (IDN)
Domains like 例え.jp use Punycode encoding (xn--r8jz45g.jp). Always validate against the Punycode form to avoid encoding mismatches.
Server-Side Verification
Client-side domain extraction (window.location.hostname) can be spoofed in browser extensions or modified clients. For critical validation, verify the domain server-side using:
- Referer/Origin headers — Check the HTTP headers for the requesting domain
- DNS TXT records — Require customers to add a TXT record proving domain ownership
- Server-side callbacks — Your customer's server validates, not the browser
// Server-side origin verification
const validateRequest = (request) => {
const origin = request.headers.get('Origin') || request.headers.get('Referer')
if (!origin) return { valid: false, error: 'Missing origin header' }
const requestDomain = new URL(origin).hostname
const rootDomain = getRootDomain(requestDomain)
// Check if root domain is authorized for this license
return checkLicenseDomain(licenseKey, rootDomain)
}
Domain Activation Limits
Set per-license domain limits that scale with your pricing tiers:
| Plan | Domain Limit | Best For |
|---|---|---|
| Builder (Free) | 1 domain | Side projects, testing |
| Starter | 3 domains | Individual developers |
| Professional | 10 domains | Small teams, agencies |
| Business | 50 domains | Growing companies |
| Enterprise | Unlimited | Large organizations |
Performance: Edge Validation
Domain validation runs on every page load or API call from your customer's application. Latency must be under 10ms. Deploy your validation logic at the edge — close to your customers — with aggressive caching of license-domain mappings.
- Cache valid licenses for 5-15 minutes at each edge location
- Cache invalid results for 1 minute (allows quick recovery after activation)
- Use stale-while-revalidate — serve cached results while refreshing in the background
Ship licensing in your next release
5 licenses, 500 validations/month, full API access. Set up in under 5 minutes — no credit card required.