| name | domain-management |
| description | Configure domain routing and Cloudflare DNS for sgcarstrends.com and subdomains. Use when adding new services, updating domain patterns, or debugging DNS issues. |
| allowed-tools | Read, Edit, Grep, Glob |
Domain Management Skill
This skill helps you manage domains and DNS configuration for the SG Cars Trends platform.
When to Use This Skill
- Adding new subdomains
- Configuring SSL certificates
- Updating DNS records
- Debugging domain resolution issues
- Setting up custom domains for services
- Managing Cloudflare settings
- Troubleshooting SSL/TLS errors
Domain Architecture
Domain Structure
sgcarstrends.com # Production web app
staging.sgcarstrends.com # Staging web app
dev.sgcarstrends.com # Development web app
api.sgcarstrends.com # Production API
api.staging.sgcarstrends.com # Staging API
api.dev.sgcarstrends.com # Development API
admin.sgcarstrends.com # Admin app (future)
docs.sgcarstrends.com # Documentation (future)
Naming Convention
Web Apps:
- Production: Apex domain (
sgcarstrends.com) - Other envs:
{stage}.sgcarstrends.com
Backend Services:
- Pattern:
{service}.{stage}.sgcarstrends.com - Production service:
{service}.sgcarstrends.com(no stage prefix)
Examples:
api.sgcarstrends.com # Production API
api.staging.sgcarstrends.com # Staging API
websocket.sgcarstrends.com # Production WebSocket service
websocket.dev.sgcarstrends.com # Dev WebSocket service
DNS Provider: Cloudflare
Why Cloudflare?
- Free SSL certificates
- DDoS protection
- CDN capabilities
- Fast DNS propagation
- Analytics and logs
- Page Rules for optimization
DNS Management
Cloudflare manages DNS records, AWS Route53 is for SSL certificate validation only.
SST Domain Configuration
DNS Stack
// infra/dns.ts
import { StackContext } from "sst/constructs";
import * as route53 from "aws-cdk-lib/aws-route53";
export function DNS({ stack }: StackContext) {
// Import existing hosted zone (created in Route53)
const hostedZone = route53.HostedZone.fromLookup(stack, "HostedZone", {
domainName: "sgcarstrends.com",
});
stack.addOutputs({
HostedZoneId: hostedZone.hostedZoneId,
HostedZoneName: hostedZone.zoneName,
});
return { hostedZone };
}
Web App Domain
// infra/web.ts
import { StackContext, NextjsSite, use } from "sst/constructs";
import { DNS } from "./dns";
export function Web({ stack, app }: StackContext) {
const { hostedZone } = use(DNS);
const web = new NextjsSite(stack, "web", {
path: "apps/web",
customDomain: {
domainName: app.stage === "production"
? "sgcarstrends.com" // Apex domain for prod
: `${app.stage}.sgcarstrends.com`, // Subdomain for others
hostedZone: hostedZone.zoneName,
},
});
stack.addOutputs({
WebUrl: web.url,
WebDomain: web.customDomainUrl,
});
return { web };
}
API Domain
// infra/api.ts
import { StackContext, Function, use } from "sst/constructs";
import { DNS } from "./dns";
export function API({ stack, app }: StackContext) {
const { hostedZone } = use(DNS);
const api = new Function(stack, "api", {
handler: "apps/api/src/index.handler",
url: {
domain: {
domainName: app.stage === "production"
? "api.sgcarstrends.com"
: `api.${app.stage}.sgcarstrends.com`,
hostedZone: hostedZone.zoneName,
},
},
});
stack.addOutputs({
ApiUrl: api.url,
ApiDomain: api.url,
});
return { api };
}
Cloudflare Configuration
DNS Records
Cloudflare DNS records point to AWS CloudFront or Lambda Function URLs:
Type Name Value Proxy
CNAME sgcarstrends.com d111111abcdef8.cloudfront.net ✓ Proxied
CNAME staging d222222abcdef8.cloudfront.net ✓ Proxied
CNAME dev d333333abcdef8.cloudfront.net ✓ Proxied
CNAME api abc123.lambda-url.ap-southeast-1.on.aws ✓ Proxied
CNAME api.staging def456.lambda-url.ap-southeast-1.on.aws ✓ Proxied
SSL/TLS Settings
Encryption Mode: Full (strict)
- Encrypts traffic from browser to Cloudflare
- Encrypts traffic from Cloudflare to origin (AWS)
- Validates origin certificate
Edge Certificates:
- Automatically provisioned by Cloudflare
- Covers
*.sgcarstrends.comandsgcarstrends.com - Auto-renewed
Page Rules
Rule 1: Cache API responses
URL: api.sgcarstrends.com/*
Settings:
- Cache Level: Standard
- Edge Cache TTL: 1 hour
Rule 2: Always use HTTPS
URL: *sgcarstrends.com/*
Settings:
- Always Use HTTPS: On
Rule 3: Security headers
URL: sgcarstrends.com/*
Settings:
- Security Headers: On
- HSTS: Enabled
SSL Certificate Management
AWS Certificate Manager
SST automatically requests certificates from ACM:
// Automatic when using customDomain
const web = new NextjsSite(stack, "web", {
customDomain: {
domainName: "sgcarstrends.com",
hostedZone: hostedZone.zoneName,
},
});
// SST will:
// 1. Request certificate from ACM
// 2. Add DNS validation records to Route53
// 3. Wait for validation
// 4. Attach certificate to CloudFront/Lambda
Manual Certificate Request
If needed:
# Request certificate
aws acm request-certificate \
--domain-name sgcarstrends.com \
--subject-alternative-names *.sgcarstrends.com \
--validation-method DNS \
--region us-east-1 # CloudFront requires us-east-1
# Get validation records
aws acm describe-certificate \
--certificate-arn arn:aws:acm:... \
--region us-east-1
Add validation CNAME records to Cloudflare DNS.
Adding New Subdomains
1. Define in SST
// infra/new-service.ts
import { StackContext, Function, use } from "sst/constructs";
import { DNS } from "./dns";
export function NewService({ stack, app }: StackContext) {
const { hostedZone } = use(DNS);
const service = new Function(stack, "new-service", {
handler: "apps/new-service/src/index.handler",
url: {
domain: {
domainName: app.stage === "production"
? "newservice.sgcarstrends.com"
: `newservice.${app.stage}.sgcarstrends.com`,
hostedZone: hostedZone.zoneName,
},
},
});
return { service };
}
2. Deploy
npx sst deploy --stage production
SST will:
- Request ACM certificate
- Create Route53 records for validation
- Wait for validation
- Create Lambda Function URL or CloudFront distribution
- Output the AWS resource URL
3. Add Cloudflare DNS
Get the AWS URL from SST outputs, then:
- Go to Cloudflare dashboard
- Select
sgcarstrends.comdomain - Click "DNS" → "Add record"
- Type: CNAME
- Name:
newservice(ornewservice.staging) - Target: The AWS URL (CloudFront or Lambda)
- Proxy status: Proxied (orange cloud)
- TTL: Auto
- Save
4. Verify
# Check DNS resolution
dig newservice.sgcarstrends.com
# Test HTTPS
curl -I https://newservice.sgcarstrends.com
Debugging DNS Issues
Check DNS Propagation
# Check Cloudflare DNS
dig @1.1.1.1 sgcarstrends.com
dig @1.1.1.1 api.sgcarstrends.com
# Check global propagation
dig sgcarstrends.com @8.8.8.8 # Google DNS
dig sgcarstrends.com @1.1.1.1 # Cloudflare DNS
# Or use online tools
# https://www.whatsmydns.net/#CNAME/api.sgcarstrends.com
Check SSL Certificate
# Check certificate details
openssl s_client -connect sgcarstrends.com:443 -servername sgcarstrends.com
# Check certificate expiry
echo | openssl s_client -connect sgcarstrends.com:443 -servername sgcarstrends.com 2>/dev/null | openssl x509 -noout -dates
Test Domain Resolution
# Test HTTP → HTTPS redirect
curl -I http://sgcarstrends.com
# Test HTTPS
curl -I https://sgcarstrends.com
# Test API endpoint
curl -I https://api.sgcarstrends.com/health
Common Issues
Issue: DNS not resolving Solutions:
- Check Cloudflare DNS record exists
- Verify record type (CNAME vs A)
- Check proxy status (should be Proxied)
- Wait for propagation (usually < 5 minutes)
Issue: SSL certificate error Solutions:
- Check Cloudflare SSL mode is "Full (strict)"
- Verify ACM certificate is issued
- Check certificate covers the subdomain
- Verify Route53 has validation records
Issue: 522 error (Connection timed out) Solutions:
- Check Lambda/CloudFront is running
- Verify security groups allow HTTPS
- Check origin server is responding
- Review Cloudflare → Origin connection
Issue: Redirecting to wrong domain Solutions:
- Check SST
customDomainconfiguration - Verify Cloudflare page rules
- Check application redirect logic
CORS Configuration
When API and Web are on different domains:
// apps/api/src/index.ts
import { Hono } from "hono";
import { cors } from "hono/cors";
const app = new Hono();
app.use("/*", cors({
origin: [
"https://sgcarstrends.com",
"https://staging.sgcarstrends.com",
"https://dev.sgcarstrends.com",
"http://localhost:3001", // Local development
],
credentials: true,
}));
Monitoring
Cloudflare Analytics
- Go to Cloudflare dashboard
- Select domain
- Click "Analytics & Logs"
View:
- Traffic stats
- Performance metrics
- Security events
- Cache analytics
AWS Route53 Health Checks
// infra/monitoring.ts
import { StackContext } from "sst/constructs";
import * as route53 from "aws-cdk-lib/aws-route53";
export function Monitoring({ stack }: StackContext) {
new route53.CfnHealthCheck(stack, "ApiHealthCheck", {
healthCheckConfig: {
type: "HTTPS",
fullyQualifiedDomainName: "api.sgcarstrends.com",
port: 443,
resourcePath: "/health",
requestInterval: 30,
failureThreshold: 3,
},
});
}
Domain Migration
Migrating to New Domain
- Set up new domain in Cloudflare
- Deploy to new domain:
customDomain: { domainName: "new-domain.com", hostedZone: newHostedZone.zoneName, } - Test new domain thoroughly
- Set up redirects from old to new:
// Cloudflare page rule URL: old-domain.com/* Forwarding URL: 301 - https://new-domain.com/$1 - Update all references
- Monitor traffic
- Keep old domain for 6-12 months
Apex Domain Considerations
Using Apex Domain
The project uses apex domain (sgcarstrends.com) for production:
Advantages:
- Shorter, cleaner URL
- Better for branding
- SEO benefits
Challenges:
- CNAME at apex not allowed by DNS spec
- Solution: Cloudflare CNAME flattening
Cloudflare CNAME Flattening
Cloudflare automatically flattens CNAMEs at apex:
# This works with Cloudflare:
CNAME sgcarstrends.com → d111111abcdef8.cloudfront.net
# Cloudflare resolves it to:
A sgcarstrends.com → 104.21.x.x
A sgcarstrends.com → 172.67.x.x
Subdomain Strategy
Current Subdomains
Web apps:
sgcarstrends.com # Production
staging.sgcarstrends.com # Staging
dev.sgcarstrends.com # Development
API:
api.sgcarstrends.com # Production
api.staging... # Staging
api.dev... # Development
Future:
admin.sgcarstrends.com # Admin panel
docs.sgcarstrends.com # Documentation
status.sgcarstrends.com # Status page
Adding Future Services
Follow the pattern:
- Production:
{service}.sgcarstrends.com - Non-production:
{service}.{stage}.sgcarstrends.com
Security Headers
Configure in Cloudflare or application:
// apps/web/next.config.js
const securityHeaders = [
{
key: "X-DNS-Prefetch-Control",
value: "on",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Referrer-Policy",
value: "origin-when-cross-origin",
},
];
const nextConfig = {
async headers() {
return [
{
source: "/:path*",
headers: securityHeaders,
},
];
},
};
References
- Cloudflare Docs: https://developers.cloudflare.com
- AWS Route53: https://docs.aws.amazon.com/route53
- SST Custom Domains: https://docs.sst.dev/custom-domains
- Related files:
infra/dns.ts- DNS configurationinfra/CLAUDE.md- Infrastructure documentation
Best Practices
- Use Cloudflare Proxy: Enable orange cloud for DDoS protection
- Full (Strict) SSL: Always use strict SSL mode
- HTTPS Only: Enforce HTTPS with page rules
- Consistent Naming: Follow
{service}.{stage}.domainpattern - Certificate Management: Let SST handle ACM certificates
- DNS TTL: Use Auto for active development, higher for stable
- Monitoring: Set up health checks for critical domains
- Documentation: Document all subdomains and their purpose