Best practices for setting up cross-cloud applications using shared IP addresses to ensure your traffic remains trusted.
Static outbound IP egress control
The most robust way to prevent blocking is to ensure your serverless functions always use a dedicated, static elastic IP (AWS) or reserved external IP (GCP) that you own and control.
AWS implementation (Lambda)
By default, Lambda runs in an AWS-managed VPC with public IPs. To fix this:
- VPC integration: Move your Lambda into a private subnet within your own VPC.
- NAT gateway: Route the private subnet's outbound traffic (0.0.0.0/0) through a NAT gateway located in a public subnet.
- Elastic IP: Associate a dedicated elastic IP with that NAT gateway.
GCP implementation (Cloud Functions and Cloud Run)
- Serverless VPC access: Create a VPC Connector to link your serverless service to your VPC.
- Cloud NAT: Create a Cloud Router and a Cloud NAT gateway.
- Static IP assignment: Configure the Cloud NAT to use a manual IP allocation and assign a reserved static external IP.
- Egress setting: Set the function egress setting to all-traffic to ensure all external calls, not only internal VPC calls, go through the NAT.
Private connectivity
For commercetools specifically, you can bypass the public internet entirely.
If you are on AWS, check with your Customer Success Manager regarding the availability of PrivateLink for your specific region, which functions similarly to PSC for AWS environments.
Client-side retry and rotation
If you cannot change the infrastructure, you can implement these client-side patterns:
- Declare a global variable (for example,
IS_IP_VERIFIED) to track the status. - If you detect a
403 Forbiddenor a connection timeout that indicates an IP block: exit immediately.
Since serverless containers are reused, you may want to force a cold start by changing the global variable programmatically, which often triggers the allocation of a new underlying host or IP address. - If the application returns
500(GCP) or502 Bad Gateway(AWS), let the client retry automatically.
Implement exponential backoff with jitter on the client. Since the Lambda instance was terminated by your code (for example, by exiting the process), the cloud provider will be forced to provision a new instance for the retry, which will ideally land on an unblocked IP address.
import { exit } from 'process';
// --- CONFIGURATION ---
const TARGET_URL = 'https://api.europe-west1.gcp.commercetools.com/';
// Global variable acts as a "State Cache" to ensure we only test once per worker
let isIpVerified = false;
// Helper to get current IP (for logging only)
async function getCurrentIp() {
try {
const res = await fetch('https://checkip.amazonaws.com', { signal: AbortSignal.timeout(2000) });
return (await res.text()).trim();
} catch (error) {
return "Unknown";
}
}
async function runCanaryTest() {
/**
* Sends an OPTIONS request to the target.
* Returns:
* - true: If connection succeeds or error is NOT 403.
* - false: ONLY if a 403 Forbidden is received.
*/
try {
const response = await fetch(TARGET_URL, {
method: 'OPTIONS',
headers: { 'User-Agent': 'AWS-Lambda-Canary-Check' },
signal: AbortSignal.timeout(3000) // 3-second timeout
});
// CHECK: Only fail specifically on 403
if (response.status === 403) {
return false;
}
// Treat 200, 404, 500, etc., as "Safe" to avoid killing workers on server glitches
return true;
} catch (error) {
// If connection completely fails (timeout/DNS), assume safe or fail open
return true;
}
}
export const handler = async (event) => {
// 1. Warm Start Optimization (Skip check if already verified)
if (isIpVerified) {
return businessLogic(event);
}
// 2. Perform Canary Test (Cold Start Only)
const isSafe = await runCanaryTest();
// 3. Handle Failure (Only log if Bad IP is detected)
if (!isSafe) {
const currentIp = await getCurrentIp();
console.log(`CRITICAL: Bad IP Detected (${currentIp}). Endpoint returned 403. Terminating Worker.`);
// Kill the worker immediately to force a new IP on the client's retry
exit(1);
}
// 4. Success Path (Silent)
isIpVerified = true;
return businessLogic(event);
};
function businessLogic(event) {
return {
statusCode: 200,
body: JSON.stringify('Request processed successfully')
};
}
Limitations and considerations
Impact and summary
- Deterministic success: You avoid unpredictable IP address changes.
- No latency penalty: No extra HTTP calls during cold starts.
Sanitize outbound request headers
X-Forwarded-For or cookies to the commercetools API, cloud providers may factor this data into geo-location, IP reputation, or sanctions checks. This can affect whether your backend IP address is treated as trusted.Strip the following headers from all outbound requests to the commercetools API:
X-Forwarded-ForTrue-Client-IPForwardedCookieRefererVia- Any other client-identifying headers
Authorization, Content-Type, X-Correlation-ID) and drop everything else by default. For the most robust solution, use a static egress IP so your traffic is always recognized as trusted regardless of user origin.Comparison
| Approach | Complexity | Reliability |
|---|---|---|
| Private connectivity | Higher | Highest (no public internet) |
| Static outbound IP | Medium | High (standard best practice) |
| Client-side Retry/Rotation | Low | Low (reactive, not proactive) |