Skip to main content

Subdomain-Based Routing

ForgeX uses subdomain-based routing to provide true microservices isolation with shared authentication. This architecture decision record explains why and how.

Architecture Overview​

forge.precisionsiteservices.com     → Portal (authentication & app launcher)
bids.precisionsiteservices.com → Bids Service
projects.precisionsiteservices.com → Projects Service
field.precisionsiteservices.com → Field Service

Each subdomain points to the same Load Balancer IP (34.160.139.39), which routes requests based on the Host header.

Why Subdomain Routing?​

Industry Standard​

Major SaaS platforms use subdomain architecture:

🎬

Netflix

www.netflix.com / api.netflix.com

💳

Stripe

dashboard.stripe.com / api.stripe.com

💬

Slack

app.slack.com / api.slack.com

🐙

GitHub

github.com / api.github.com

This pattern is battle-tested at scale and well-understood by developers.

Technical Benefits​

🛡️True Service Isolation

Each service is independently deployable:

  • Deploy Bids service without affecting Projects
  • Scale services independently based on load
  • Monitor each service separately
  • Different resource limits per service
# Deploy only Bids service
gcloud run deploy forge-bids-backend --region us-south1

# Projects service unaffected
☁️Simplified Infrastructure

Cloud Storage buckets serve index.html naturally:

  • No path rewriting required
  • No complex Load Balancer path rules
  • Each bucket maps to one subdomain
gs://forge-475221-portal  → forge.precisionsiteservices.com
gs://forge-475221-bids → bids.precisionsiteservices.com
🔒Better Security

Subdomain boundaries provide natural security isolation:

  • XSS Isolation: Compromise in one service doesn't directly affect others
  • Independent CSP: Each service has its own Content Security Policy
  • Cookie Scope: Cookies can be service-specific or shared (.precisionsiteservices.com)
  • CORS Control: Fine-grained cross-origin rules
🚀Future-Proof

Architecture scales without breaking changes:

  • Add new services by adding DNS records
  • Spin off services to separate infrastructure
  • Sell or transfer services by changing DNS
  • Per-service SSL certificates if needed

Authentication Flow​

Subdomain routing enables a clean authentication pattern via SuperTokens:

🔍 Click diagram to expand
info

Key Insight: SuperTokens session cookies set with domain=.precisionsiteservices.com are automatically sent to all subdomains. Users authenticate once at the Portal via SuperTokens, then seamlessly access all services without re-authentication.

Security Implementation​

HTTP-Only Cookies​

res.cookie('accessToken', accessToken, {
httpOnly: true, // Prevents XSS access
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
domain: '.precisionsiteservices.com', // Works across all subdomains
maxAge: 15 * 60 * 1000 // 15 minutes
});

Security Headers​

Each service sets appropriate security headers:

Content-Security-Policy: default-src 'self' *.precisionsiteservices.com
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff

CORS Configuration​

Backend services whitelist specific subdomains:

const allowedOrigins = [
'https://forge.precisionsiteservices.com',
'https://bids.precisionsiteservices.com',
'https://projects.precisionsiteservices.com',
'https://field.precisionsiteservices.com'
];

app.use(cors({
origin: (origin, callback) => {
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true // Allow cookies
}));

Infrastructure Configuration​

DNS Setup​

Four A records pointing to the same Load Balancer IP:

forge.precisionsiteservices.com     A  34.160.139.39
bids.precisionsiteservices.com A 34.160.139.39
projects.precisionsiteservices.com A 34.160.139.39
field.precisionsiteservices.com A 34.160.139.39
info

All subdomains point to the same IP. The Load Balancer routes based on the Host header.

SSL Certificate​

Managed SSL certificate includes all subdomains:

gcloud compute ssl-certificates create forge-ssl-cert \
--domains=forge.precisionsiteservices.com,bids.precisionsiteservices.com,projects.precisionsiteservices.com,field.precisionsiteservices.com

Google automatically renews the certificate before expiration.

Load Balancer URL Map​

Host-based routing rules:

hostRules:
- hosts:
- forge.precisionsiteservices.com
pathMatcher: portal-matcher

- hosts:
- bids.precisionsiteservices.com
pathMatcher: bids-matcher

- hosts:
- projects.precisionsiteservices.com
pathMatcher: projects-matcher

- hosts:
- field.precisionsiteservices.com
pathMatcher: field-matcher

pathMatchers:
- name: portal-matcher
defaultService: backend-bucket-portal

- name: bids-matcher
defaultService: backend-bucket-bids
pathRules:
- paths: ['/api/*']
service: backend-service-bids
tip

Host rules are simpler and more reliable than path rules. Each host gets a dedicated path matcher.

vs Path-Based Routing​

We initially considered path-based routing but switched to subdomains:

Pros:

  • Industry standard
  • True service isolation
  • Simpler infrastructure
  • Better security boundaries
  • Future-proof

Cons:

  • Multiple DNS records (minimal)
  • Longer URLs (mitigated by Portal)

URLs:

https://forge.precisionsiteservices.com
https://bids.precisionsiteservices.com
📖

Architecture Decision Record

Read the full ADR: ARCHITECTURE_DECISION_SUBDOMAIN_ROUTING.md

User Experience​

Users interact with the Portal, not individual subdomains:

  1. Bookmark: forge.precisionsiteservices.com (one URL to remember)
  2. Login: Enter credentials once
  3. Navigate: Click app tiles in Portal
  4. Seamless: SuperTokens session cookies work across all apps
  5. Direct Access: Can bookmark individual apps (e.g., bids.precisionsiteservices.com)

The subdomain architecture is invisible to users while providing technical benefits.

Adding New Services​

To add a new service (e.g., Inventory):

1
Add DNS Record
# Point to same Load Balancer IP
inventory.precisionsiteservices.com A 34.160.139.39
2
Update SSL Certificate
gcloud compute ssl-certificates create forge-ssl-cert-v2 \
--domains=forge.precisionsiteservices.com,...,inventory.precisionsiteservices.com

# Update Load Balancer to use new cert
gcloud compute target-https-proxies update forge-https-proxy \
--ssl-certificates=forge-ssl-cert-v2
3
Create Cloud Storage Bucket
gcloud storage buckets create gs://forge-475221-inventory \
--location=us-south1 \
--uniform-bucket-level-access
4
Add Load Balancer Host Rule
- hosts:
- inventory.precisionsiteservices.com
pathMatcher: inventory-matcher
5
Deploy Service

Deploy backend to Cloud Run, frontend to Cloud Storage.

6
Add to Portal

Add "Inventory" tile to Portal app launcher.

info

Existing services are unaffected when adding new ones. Deploy with confidence!

Troubleshooting​

Cookies not working across subdomains

Verify the cookie domain is set correctly:

// ✅ Correct (works on all subdomains)
domain: '.precisionsiteservices.com'

// ❌ Wrong (only works on exact domain)
domain: 'forge.precisionsiteservices.com'

Check in browser DevTools → Application → Cookies.

CORS errors between services

Ensure all subdomains are in the CORS whitelist:

const allowedOrigins = [
'https://forge.precisionsiteservices.com',
'https://bids.precisionsiteservices.com',
// Add all subdomains!
];

Check browser console for specific origin that was rejected.

Load Balancer routing to wrong service

Verify host rules are configured correctly:

gcloud compute url-maps describe forge-lb --format=yaml

Check the Host header matches your subdomain exactly.

SSL certificate errors

Ensure all subdomains are in the certificate:

gcloud compute ssl-certificates describe forge-ssl-cert \
--format="value(managed.domains)"

If missing, create a new certificate with all domains and update the proxy.

Best Practices​

🚪

Portal-First Design

Users should always enter through the Portal, not directly to services. This ensures:

  • Centralized authentication
  • Consistent branding
  • Role-based app access
  • Analytics on app usage
🔒

Cookie Security

Always use httpOnly, secure, and sameSite flags:

{ httpOnly: true, secure: true, sameSite: 'strict' }

This prevents XSS and CSRF attacks.

✅

CORS Whitelist

Never use origin: '*' in production. Whitelist specific subdomains only.

🚀

Service Independence

Each service should be deployable independently. Avoid tight coupling between services.

Next Steps​