Custom Domains & SSL
Configure custom domains with Cloud DNS, set up load balancers, and enable HTTPS with managed SSL certificates.
What you'll accomplish
Your application accessible via custom domain with HTTPS
Prerequisites
- Application deployed to Cloud Run or Compute Engine
- Domain name you own
- Access to domain's DNS settings
Domain Options in GCP
GCP offers multiple ways to connect custom domains:
| Method | Best For | SSL | Complexity |
|---|---|---|---|
| Cloud Run domain mapping | Simple Cloud Run apps | Auto-managed | Low |
| Cloud Load Balancer | Production apps, multiple backends | Auto-managed | Medium |
| Cloud DNS | Full DNS management | Via Load Balancer | Medium |
| External DNS | Keep existing DNS provider | Manual or via LB | Varies |
Option 1: Cloud Run Domain Mapping (Simplest)
For Cloud Run services, the easiest approach is direct domain mapping.
Step 1: Verify Domain Ownership
bash# Start domain verification gcloud domains verify example.com
This opens a browser to verify ownership via:
- DNS TXT record
- HTML file upload
- Google Analytics
- Google Search Console
Step 2: Map Domain to Cloud Run
bash# Map custom domain gcloud run domain-mappings create \ --service=myapp \ --domain=myapp.example.com \ --region=us-central1
Step 3: Configure DNS Records
Get the required DNS records:
bashgcloud run domain-mappings describe \ --domain=myapp.example.com \ --region=us-central1
Add these records at your DNS provider:
| Type | Name | Value |
|---|---|---|
| CNAME | myapp | ghs.googlehosted.com |
Or for apex domain (example.com):
| Type | Name | Value |
|---|---|---|
| A | @ | (IP addresses from gcloud output) |
| AAAA | @ | (IPv6 addresses from gcloud output) |
Step 4: Verify Mapping
bash# Check status gcloud run domain-mappings describe \ --domain=myapp.example.com \ --region=us-central1 # Status should change from "pending" to "ready"
Note
SSL certificates are automatically provisioned. This can take 15-60 minutes.
Option 2: Cloud DNS
Manage your DNS entirely within GCP.
Step 1: Enable Cloud DNS API
bashgcloud services enable dns.googleapis.com
Step 2: Create a DNS Zone
bashgcloud dns managed-zones create my-zone \ --dns-name="example.com." \ --description="DNS zone for example.com"
Note the trailing dot after the domain name.
Step 3: Get Nameservers
bashgcloud dns managed-zones describe my-zone \ --format="value(nameServers)"
Output example:
ns-cloud-a1.googledomains.com.
ns-cloud-a2.googledomains.com.
ns-cloud-a3.googledomains.com.
ns-cloud-a4.googledomains.com.
Step 4: Update Domain Registrar
At your domain registrar (GoDaddy, Namecheap, Google Domains, etc.):
- Find nameserver settings
- Replace existing nameservers with Google's
- Save changes
Note
DNS propagation can take 24-48 hours. During this time, your site may be intermittently accessible.
Step 5: Add DNS Records
bash# Start a transaction gcloud dns record-sets transaction start --zone=my-zone # Add A record gcloud dns record-sets transaction add \ --zone=my-zone \ --name="example.com." \ --ttl=300 \ --type=A \ "34.102.136.180" # Add CNAME for www gcloud dns record-sets transaction add \ --zone=my-zone \ --name="www.example.com." \ --ttl=300 \ --type=CNAME \ "example.com." # Execute the transaction gcloud dns record-sets transaction execute --zone=my-zone
View DNS Records
bashgcloud dns record-sets list --zone=my-zone
Update DNS Records
bash# Remove old record and add new one gcloud dns record-sets transaction start --zone=my-zone gcloud dns record-sets transaction remove \ --zone=my-zone \ --name="example.com." \ --ttl=300 \ --type=A \ "OLD_IP" gcloud dns record-sets transaction add \ --zone=my-zone \ --name="example.com." \ --ttl=300 \ --type=A \ "NEW_IP" gcloud dns record-sets transaction execute --zone=my-zone
Option 3: Cloud Load Balancer with SSL
For production applications needing advanced features.
Why Use a Load Balancer?
- Multiple backends - Route to different services
- Global distribution - Edge caching worldwide
- Advanced routing - Path-based, header-based rules
- DDoS protection - Cloud Armor integration
- Managed SSL - Auto-renewed certificates
Architecture
Users → Cloud Load Balancer → Cloud Run / GKE / VMs
│
├── SSL Certificate
├── URL Maps (routing)
└── Backend Services
Step 1: Reserve Static IP
bash# Reserve global static IP gcloud compute addresses create myapp-ip \ --global \ --ip-version=IPV4 # Get the IP address gcloud compute addresses describe myapp-ip \ --global \ --format="get(address)"
Step 2: Create Serverless NEG (for Cloud Run)
bash# Create Network Endpoint Group gcloud compute network-endpoint-groups create myapp-neg \ --region=us-central1 \ --network-endpoint-type=serverless \ --cloud-run-service=myapp
Step 3: Create Backend Service
bash# Create backend service gcloud compute backend-services create myapp-backend \ --global \ --load-balancing-scheme=EXTERNAL_MANAGED # Add NEG to backend gcloud compute backend-services add-backend myapp-backend \ --global \ --network-endpoint-group=myapp-neg \ --network-endpoint-group-region=us-central1
Step 4: Create URL Map
bash# Create URL map (routes requests to backends) gcloud compute url-maps create myapp-urlmap \ --default-service=myapp-backend
Step 5: Create SSL Certificate
bash# Create managed SSL certificate gcloud compute ssl-certificates create myapp-cert \ --domains=example.com,www.example.com \ --global
Step 6: Create HTTPS Proxy
bash# Create target HTTPS proxy gcloud compute target-https-proxies create myapp-https-proxy \ --ssl-certificates=myapp-cert \ --url-map=myapp-urlmap
Step 7: Create Forwarding Rule
bash# Create forwarding rule (connects IP to proxy) gcloud compute forwarding-rules create myapp-https-rule \ --global \ --target-https-proxy=myapp-https-proxy \ --address=myapp-ip \ --ports=443
Step 8: Add HTTP to HTTPS Redirect
bash# Create URL map for redirect gcloud compute url-maps import http-to-https-redirect \ --source=/dev/stdin <<EOF name: http-to-https-redirect defaultUrlRedirect: redirectResponseCode: MOVED_PERMANENTLY_DEFAULT httpsRedirect: True EOF # Create HTTP proxy gcloud compute target-http-proxies create myapp-http-proxy \ --url-map=http-to-https-redirect # Create HTTP forwarding rule gcloud compute forwarding-rules create myapp-http-rule \ --global \ --target-http-proxy=myapp-http-proxy \ --address=myapp-ip \ --ports=80
Step 9: Update DNS
Point your domain to the static IP:
bash# Get the IP gcloud compute addresses describe myapp-ip \ --global \ --format="get(address)" # Add A record at your DNS provider: # example.com → IP_ADDRESS # www.example.com → IP_ADDRESS (or CNAME to example.com)
Step 10: Verify SSL Certificate
bash# Check certificate status gcloud compute ssl-certificates describe myapp-cert \ --global \ --format="get(managed.status)" # Status should be ACTIVE (may take 10-60 minutes)
Advanced Load Balancer Configuration
Path-Based Routing
Route different paths to different services:
bash# Create URL map with path rules gcloud compute url-maps create myapp-urlmap \ --default-service=web-backend gcloud compute url-maps add-path-matcher myapp-urlmap \ --path-matcher-name=api-matcher \ --default-service=web-backend \ --path-rules="/api/*=api-backend,/static/*=cdn-backend"
Host-Based Routing
Route different subdomains to different services:
bashgcloud compute url-maps add-host-rule myapp-urlmap \ --hosts=api.example.com \ --path-matcher-name=api-matcher gcloud compute url-maps add-host-rule myapp-urlmap \ --hosts=admin.example.com \ --path-matcher-name=admin-matcher
Cloud CDN
Enable caching at Google's edge:
bashgcloud compute backend-services update myapp-backend \ --global \ --enable-cdn \ --cache-mode=CACHE_ALL_STATIC
Cloud Armor (DDoS Protection)
bash# Create security policy gcloud compute security-policies create myapp-security \ --description="Security policy for myapp" # Add rate limiting rule gcloud compute security-policies rules create 1000 \ --security-policy=myapp-security \ --expression="true" \ --action="rate-based-ban" \ --rate-limit-threshold-count=100 \ --rate-limit-threshold-interval-sec=60 \ --ban-duration-sec=300 # Apply to backend gcloud compute backend-services update myapp-backend \ --global \ --security-policy=myapp-security
SSL Certificate Options
Google-Managed (Recommended)
bash# Automatic provisioning and renewal gcloud compute ssl-certificates create myapp-cert \ --domains=example.com,www.example.com \ --global
Self-Managed
bash# Upload your own certificate gcloud compute ssl-certificates create myapp-cert \ --certificate=path/to/cert.pem \ --private-key=path/to/key.pem \ --global
Certificate Status
bash# Check provisioning status gcloud compute ssl-certificates list # Detailed status gcloud compute ssl-certificates describe myapp-cert --global
Status values:
PROVISIONING- Certificate being issuedACTIVE- Ready to useFAILED_NOT_VISIBLE- DNS not pointing to load balancerFAILED_CAA_CHECKING- CAA record issue
Troubleshooting
SSL Certificate Stuck in PROVISIONING
- Check DNS: Domain must point to load balancer IP
- Check CAA records: If you have CAA records, add
pki.goog - Wait: Can take up to 60 minutes
bash# Check if DNS is correct dig example.com +short # Should return your load balancer IP # Check CAA records dig example.com CAA # Add: example.com. CAA 0 issue "pki.goog"
502 Bad Gateway
bash# Check backend health gcloud compute backend-services get-health myapp-backend --global # Check Cloud Run service is responding curl -H "Host: example.com" https://myapp-xxx-uc.a.run.app/
Mixed Content Warnings
Ensure your app generates HTTPS URLs:
javascript// Check for X-Forwarded-Proto header const protocol = req.headers['x-forwarded-proto'] || 'http'; const url = `${protocol}://${req.hostname}${req.path}`;
DNS Propagation Issues
bash# Check DNS propagation dig example.com @8.8.8.8 dig example.com @1.1.1.1 # Use DNS propagation checker # https://www.whatsmydns.net/
Cost Considerations
| Component | Cost |
|---|---|
| Cloud DNS | $0.20/zone/month + $0.40/million queries |
| Static IP | Free while attached, $0.01/hour if unused |
| Load Balancer | ~$18/month minimum + data processed |
| SSL Certificate | Free (managed) |
Note
For simple Cloud Run apps, use direct domain mapping to avoid load balancer costs. Use a load balancer when you need advanced features like CDN, multiple backends, or Cloud Armor.
Complete Example: Domain Setup for Cloud Run
Quick Setup (Domain Mapping)
bash# 1. Verify domain gcloud domains verify example.com # 2. Map domain gcloud run domain-mappings create \ --service=myapp \ --domain=myapp.example.com \ --region=us-central1 # 3. Add CNAME at DNS provider # myapp CNAME ghs.googlehosted.com # 4. Wait for SSL (15-60 minutes) gcloud run domain-mappings describe \ --domain=myapp.example.com \ --region=us-central1
Production Setup (Load Balancer)
bash# 1. Reserve IP gcloud compute addresses create myapp-ip --global # 2. Create NEG gcloud compute network-endpoint-groups create myapp-neg \ --region=us-central1 \ --network-endpoint-type=serverless \ --cloud-run-service=myapp # 3. Create backend gcloud compute backend-services create myapp-backend \ --global \ --load-balancing-scheme=EXTERNAL_MANAGED gcloud compute backend-services add-backend myapp-backend \ --global \ --network-endpoint-group=myapp-neg \ --network-endpoint-group-region=us-central1 # 4. Create SSL cert gcloud compute ssl-certificates create myapp-cert \ --domains=example.com,www.example.com \ --global # 5. Create URL map gcloud compute url-maps create myapp-urlmap \ --default-service=myapp-backend # 6. Create HTTPS proxy gcloud compute target-https-proxies create myapp-https-proxy \ --ssl-certificates=myapp-cert \ --url-map=myapp-urlmap # 7. Create forwarding rule gcloud compute forwarding-rules create myapp-https-rule \ --global \ --target-https-proxy=myapp-https-proxy \ --address=myapp-ip \ --ports=443 # 8. Update DNS to point to IP gcloud compute addresses describe myapp-ip --global --format="get(address)"
Note
Checkpoint: Access your application via your custom domain over HTTPS. The SSL certificate should be valid and the connection secure.