Best practices for setting up cross-cloud applications using shared IP addresses to ensure your traffic remains trusted.
Ask about this Page
Copy for LLM
View as Markdown
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.
Result: Every request to commercetools will originate from that single, static IP. You can then provide this IP to commercetools support or add it to your own allowlists.
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 hosted on GCP, commercetools offers Private Service Connect (PSC).
This allows your VPC to connect to commercetools via a private IP address that never traverses the public internet, effectively making IP reputation a non-issue.
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.
Example Lambda with Canary test
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
While currently very few IP addresses used by major cloud providers are blocked by cloud providers, you should log and monitor errors of this type to detect trends.
Should the frequency of blocked IPs increase, consider moving to a static outbound IP solution.
Impact and summary
This approach will effectively filter out a small number of blocked IP addresses at negligible cost and with no performance impact on 99.9 percent of your traffic.
Once the frequency begins to impede operations, raise a support ticket and share the blocked IP addresses so we can escalate them to our cloud provider.
While the canary test approach is free with no NAT gateway hourly charges, the static outbound IP approach is superior for enterprise applications because:
- Deterministic success: You avoid unpredictable IP address changes.
- No latency penalty: No extra HTTP calls during cold starts.
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) |