Solving the AWS CloudFront Cross-Region Certificate Puzzle with CDK
The Problem That Makes Developers Pull Their Hair Out
When you design infrastructure across multiple AWS regions, you expect each region to be self-contained — its own EKS clusters, databases, Lambdas, and certificates — all neatly separated by region.
That’s how my CDK setup was built: a clean, modular design that could deploy the same infrastructure stack in any region (ap-southeast-2, ap-south-1, etc.) just by switching configuration.
Everything worked perfectly… until CloudFront entered the picture.
The moment I added CloudFront for my frontend, deployments started failing with confusing certificate errors like:
Error: Certificate for alternate domain name is not issued by AWS Certificate Manager
Or worse:
The certificate that is attached to your distribution doesn't cover the alternate domain name
After debugging, I realized the issue wasn’t with my setup at all — it was an AWS constraint hiding in plain sight:
CloudFront requires SSL certificates to be created in us-east-1, no matter where your infrastructure lives.
That single requirement completely broke my otherwise predictable, region-specific deployment model. This post walks through how I solved it — a clean, battle-tested pattern to handle CloudFront’s cross-region certificate constraint using AWS CDK, without messy hacks or manual intervention.
Problem 1: The Hidden us-east-1 Certificate Requirement
The Problem
AWS CloudFront is a global service, but it has a peculiar requirement: SSL/TLS certificates used with CloudFront distributions must be created in the us-east-1 region. This isn't clearly stated in error messages, and developers often waste hours trying to debug certificate validation issues.
Common symptoms:
Certificate validation succeeds in your primary region but CloudFront still fails
"Certificate not found" errors despite the certificate existing
CloudFormation rollbacks with cryptic certificate-related errors
The Solution
Create a dedicated stack for us-east-1 resources that extends your domain stack:
// src/stacks/domain/us-east1-domain.stack.ts
export class USEast1DomainStack extends DomainStack {
constructor(scope: Construct, id: string, props: IStackProps) {
super(scope, id, {
...props,
description: 'Stack to create domain resources in us-east-1',
}, true);
// Enforce us-east-1 deployment
if (props.env?.region !== 'us-east-1' ||
props.config.region !== 'us-east-1') {
throw new Error('This stack must be deployed in us-east-1 region');
}
// Create wildcard certificate for CloudFront
this.createWildcardCertificate();
this.createWebAppReviewCertificates();
}
}
Why this works: By explicitly creating a separate stack for us-east-1 resources, you maintain clean separation of concerns while ensuring certificates are in the correct region.
Problem 2: Cross-Region Reference Failures
The Problem
When your main infrastructure is in ap-southeast-2 but certificates are in us-east-1, CDK throws errors:
Error: Stack "FrontendStack" cannot reference "USEast1DomainStack" across regions without crossRegionReferences enabled
Even worse, CloudFormation exports don't automatically work across regions, leading to deployment failures.
The Solution
Enable cross-region references when creating the us-east-1 stack:
// src/stages/microservices-platform.stage.ts
const usEast1DomainStack = new USEast1DomainStack(
this,
'USEast1DomainStack',
{
...props,
env: {
...props.env,
region: 'us-east-1', // Override region
},
config: {
...props.config,
region: 'us-east-1', // Update config region too
},
crossRegionReferences: true, // Critical!
},
);
// Frontend stack can now reference us-east-1 certificates
const frontendStack = new FrontendStack(this, 'FrontendStack', {
...props,
domainStack: usEast1DomainStack, // Pass the us-east-1 stack
crossRegionReferences: true,
});
Why this works: The crossRegionReferences flag tells CDK to use AWS Systems Manager Parameter Store to share values between regions, working around CloudFormation's regional limitations.
Problem 3: Stack Dependency and Deployment Order
The Problem
Complex dependency chains emerge when you have:
A domain stack in your primary region (for Route53)
A us-east-1 domain stack (for certificates)
Frontend stacks that need both
Incorrect dependency setup leads to:
Circular dependency errors
Resources created in wrong order
"Export already exists" conflicts
The Solution
Implement intelligent dependency management:
// Conditional domain stack creation based on region
let domainStack: DomainStack | undefined = undefined;
if (this.config.region !== 'us-east-1') {
domainStack = new DomainStack(this, 'DomainStack', props, true);
}
const usEast1DomainStack = new USEast1DomainStack(
this,
'USEast1DomainStack',
{ /* ... */ }
);
// Set up dependency chain
if (domainStack) {
usEast1DomainStack.addDependency(domainStack);
} else {
domainStack = usEast1DomainStack; // us-east-1 IS the domain stack
}
// Frontend depends on us-east-1 stack for certificates
const frontendStack = new FrontendStack(this, 'FrontendStack', {
...props,
domainStack: usEast1DomainStack,
crossRegionReferences: true,
});
frontendStack.addDependency(usEast1DomainStack);
Why this works: This pattern ensures:
Route53 hosted zone is created first (if needed)
Certificates are created in us-east-1 with DNS validation
Frontend resources wait for certificates to be ready
Problem 4: Managing Multiple Apps and Review Deployments
The Problem
Modern development workflows require:
Multiple frontend applications (admin portal, customer app, etc.)
Review/preview deployments for pull requests
Wildcard certificates for dynamic subdomains
Managing certificates for all these scenarios becomes complex.
The Solution
Use a combination of wildcard certificates and app-specific certificates:
// Base wildcard certificate for all standard subdomains
private createWildcardCertificate(): void {
this._wildcardCertificate = new acm.Certificate(
this,
'WildcardCertificate',
{
domainName: `*.${this.domainName}`,
validation: acm.CertificateValidation.fromDns(this.hostedZone),
}
);
}
// App-specific wildcard certificates for review deployments
private createWebAppReviewCertificates(): void {
for (const frontEndAppName in this.config.domainConfig.frontendApp) {
const enableReviews = this.config.frontendApps?.find(
(app) => app.name === frontEndAppName,
)?.enableReviews;
if (!enableReviews) continue;
const frontEndDomain =
this.config.domainConfig.frontendApp[frontEndAppName]?.subDomainName;
// Create wildcard for review apps: *.app.domain.com
const certificate = new acm.Certificate(
this,
`${frontEndDomain}-certificate`,
{
domainName: `${frontEndDomain}.${this.domainName}`,
subjectAlternativeNames: [`*.${frontEndDomain}.${this.domainName}`],
validation: acm.CertificateValidation.fromDns(this.hostedZone),
}
);
this.webappReviewCertificates.set(frontEndAppName, certificate);
}
}
Why this works: This approach provides flexibility for both stable deployments and dynamic review environments without certificate proliferation.
Problem 5: Bootstrap and Environment Configuration
The Problem
Cross-region deployments require:
Both regions to be bootstrapped
Environment-specific configurations
Proper IAM trust relationships
Missing any of these causes deployment failures that are hard to debug.
The Solution
- Bootstrap both regions before deployment:
# Bootstrap primary region
cdk bootstrap aws://ACCOUNT_ID/ap-southeast-2
# Bootstrap us-east-1 for certificates
cdk bootstrap aws://ACCOUNT_ID/us-east-1
- Use environment-specific configuration:
// src/config/environments/dev/config.dev.ts
export const AUDevConfig: IConfig = {
accountID: DEV_ACCOUNT_ID,
region: 'ap-southeast-2',
env: 'DEV',
iluminrPrimaryRegion: 'ap-southeast-2',
domainConfig: devDomainConfig,
frontendApps: devFrontendConfig,
// ... other config
};
- Consume configuration in your CDK app:
// Frontend stack integration
const frontendStack = new FrontendStack(this, 'FrontendStack', {
...props,
domainStack: usEast1DomainStack,
crossRegionReferences: true,
});
// Angular deployment using the cross-region certificate
new AngularAppDeployment(this, app.name, {
appName: app.name,
subDomainName: props.config.domainConfig.frontendApp[app.name]?.subDomainName,
rootDomain: props.config.domainConfig.domainName,
certificate: props.domainStack.wildcardCertificate, // From us-east-1
hostedZone: props.domainStack.hostedZone,
env: props.config.env,
});
Why this works: Proper bootstrapping ensures CDK has the necessary resources in both regions, while configuration management keeps your infrastructure DRY and maintainable.
Best Practices
Always validate certificates using DNS validation - It works across regions without manual intervention
Use wildcard certificates strategically - Reduces certificate count and management overhead
Tag your resources consistently - Makes debugging and cost allocation easier
Export critical values - Certificate ARNs, distribution IDs for use by CI/CD pipelines
Document region requirements - Add error checks that explicitly state region requirements
Common Pitfalls to Avoid
❌ Don't Forget to Bootstrap us-east-1
Even if your infrastructure is elsewhere, us-east-1 needs bootstrapping for certificate creation.
❌ Don't Mix Regional and Global Resources
Keep clear separation between regional stacks and us-east-1 stacks.
❌ Don't Ignore Deletion Order
When destroying stacks, delete in reverse dependency order:
# Delete frontend first
cdk destroy FrontendStack
# Then us-east-1 resources
cdk destroy USEast1DomainStack
# Finally, primary region resources
cdk destroy DomainStack
❌ Don't Hardcode Region Values
Always use configuration to manage regions:
// Bad
const stack = new Stack(this, 'Stack', { env: { region: 'us-east-1' } });
// Good
const stack = new Stack(this, 'Stack', {
env: { region: config.certificateRegion }
});
Conclusion
The CloudFront cross-region certificate requirement is a classic AWS gotcha that has frustrated countless developers. By understanding the underlying constraints and implementing a clean separation between regional and us-east-1 resources, you can build robust, multi-region CDK applications without the headache.
Key takeaways:
Always create CloudFront certificates in us-east-1
Use
crossRegionReferences: truefor cross-region stack dependenciesImplement proper dependency chains between stacks
Bootstrap all regions before deployment
Use configuration-driven infrastructure for flexibility
This pattern has been battle-tested in production environments serving millions of requests. It provides a solid foundation for building globally distributed applications with AWS CDK while maintaining clean, maintainable infrastructure code.
Remember: The complexity is in understanding the problem. Once you know the constraints, the solution is straightforward.
Have you encountered other cross-region challenges with AWS CDK? What patterns have worked for you? Share your experiences and let's build better infrastructure together.





