Netlify Functions Static IP: Outbound IP Setup
Netlify Functions run on shared serverless infrastructure with changing outbound IPs. Netlify's fixed-IP option, Private Connectivity, is an Enterprise add-on you enable through your account manager. To get a static outbound IP on any plan, call allowlisted APIs from a Netlify Function through a Fixedmark proxy URL.
Why Netlify Functions change IP
Netlify Functions run on managed serverless compute. Each cold start can land on different infrastructure, and the outbound address comes from a large shared pool. Netlify does not publish a fixed egress list for Functions on self-serve plans.
Netlify Private Connectivity
Per Netlify's docs, Private Connectivity gives builds, serverless functions, and the Netlify API a set of static IPs. It is an Enterprise add-on that also requires High-Performance Edge or High-Performance Build, and your account manager enables it. Functions using it must run in Ohio, Frankfurt, or London, and it does not cover Edge Functions. There is no self-serve option on Free, Personal, or Pro plans.
If you already have an Enterprise contract, ask your account team first: it keeps everything inside Netlify. For everyone else, a proxy is the practical route. It also lets the same IPs serve calls from Netlify and from any other host you use.
Sources for Netlify pricing
Checked October 2026. Confirm current details on the vendor's site before you decide. Prices and limits change.
Set up a static IP on Netlify
Use standard Netlify Functions for any call that needs the fixed IP. Edge Functions run on a different runtime and are not supported at launch.
Fixedmark is in early access. Proxy URLs are issued at launch. Join early access to get yours when your region opens.
Before you start
- A Netlify site with Functions in JavaScript, TypeScript, or Go.
- The Netlify CLI (
npm i -g netlify-cli), linked to the site. - A Fixedmark proxy URL (
https://APP_ID:TOKEN@mum.egress.fixedmark.com:443) and the two IPs of your pair. Both are issued at launch. Join early access to get them. - Access to the partner's or database's allowlist, or a contact who can add two IPv4 addresses for you.
Steps
- 1Add the environment variableRun
netlify env:set FIXEDMARK_PROXY_URL https://APP_ID:TOKEN@mum.egress.fixedmark.com:443 --secret. Without--scope, the variable is available to builds and functions. Limiting scopes to Functions needs a Pro or Enterprise plan. - 2Bundle undici with your functionAdd
undicitopackage.json. ImportfetchandProxyAgentfrom it and create the agent at module scope. - 3Deploy and verifyDeploy, call the IP check function below, and compare the reported IP against your Fixedmark pair.
- 4Allowlist both IPsSend both addresses to the partner. Requests are load-balanced across the pair.
// npm install undici (undici 8 needs Node.js 22.19+)
import { fetch, ProxyAgent } from "undici";
// FIXEDMARK_PROXY_URL=https://APP_ID:TOKEN@mum.egress.fixedmark.com:443
const proxy = new ProxyAgent(process.env.FIXEDMARK_PROXY_URL);
const res = await fetch("https://api.partner.example/v1/orders", {
dispatcher: proxy,
headers: { authorization: `Bearer ${process.env.PARTNER_API_KEY}` },
});
console.log(res.status, await res.json());package main
import (
"fmt"
"net/http"
"net/url"
"os"
"time"
)
func main() {
proxyURL, err := url.Parse(os.Getenv("FIXEDMARK_PROXY_URL"))
if err != nil {
panic(err)
}
client := &http.Client{
Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
Timeout: 15 * time.Second,
}
res, err := client.Get("https://api.partner.example/v1/orders")
if err != nil {
panic(err)
}
defer res.Body.Close()
fmt.Println(res.Status)
}SDKs that do not use fetch (the Stripe and Salesforce Node SDKs, got, axios) accept an HTTP agent. Pass new HttpsProxyAgent(process.env.FIXEDMARK_PROXY_URL) from the https-proxy-agent package, which supports https:// proxy URLs. With axios, also set proxy: false so it does not apply its own proxy logic. undici 8 needs Node 22.19 or later. On older Node, install undici@7. Netlify Edge Functions run on Deno at the edge. Netlify does not document a proxy option there, so treat them as unsupported at launch and keep allowlisted calls in regular Functions.
Verify the egress IP on Netlify
Check the IP before you send it to a partner. netlify dev runs functions locally with your site's variables, which tests the credentials. Then deploy a temporary function with the Node.js check and call it once. The endpoint https://fixedmark.com/api/ip returns the caller's IP as plain text, and the What's my IP tool shows the same thing in a browser.
The proxied call should print one of your two Fixedmark IPs every time. The direct call should print a different address. If both print the same platform IP, the client is not using the proxy.
# Needs curl 7.52 or later for an https:// proxy URL.
# Prints the IP the destination sees. Run it a few times:
# you should only ever see the two IPs of your pair.
curl -sS --proxy "$FIXEDMARK_PROXY_URL" https://fixedmark.com/api/ip
# Same check over SOCKS5, the path database drivers use.
curl -sS --proxy "$FIXEDMARK_SOCKS_URL" https://fixedmark.com/api/ip
# Without the proxy, for comparison. This is the platform's own IP.
curl -sS https://fixedmark.com/api/ip// A temporary route or script that reports the egress IP.
// Remove it after you have checked.
import { fetch, ProxyAgent } from "undici";
const proxy = new ProxyAgent(process.env.FIXEDMARK_PROXY_URL);
const viaProxy = await fetch("https://fixedmark.com/api/ip", { dispatcher: proxy });
const direct = await fetch("https://fixedmark.com/api/ip");
console.log({
fixedmark: (await viaProxy.text()).trim(),
platform: (await direct.text()).trim(),
});import os
import requests
proxy = os.environ["FIXEDMARK_PROXY_URL"]
via = requests.get("https://fixedmark.com/api/ip", proxies={"https": proxy}, timeout=10)
direct = requests.get("https://fixedmark.com/api/ip", timeout=10)
print({"fixedmark": via.text.strip(), "platform": direct.text.strip()})Databases: Postgres, MySQL, and MongoDB
Netlify functions cannot run a background process, so the bm tunnel CLI is not an option there. Use a driver that can dial through SOCKS5 with FIXEDMARK_SOCKS_URL. The MongoDB Node.js driver supports this with its proxyHost, proxyPort, proxyUsername, and proxyPassword options (install the socks package next to it). Go's pgx accepts a SOCKS5 dialer through DialFunc.
node-postgres, mysql2, and psycopg have no SOCKS5 option. For those, move the queries into a small long-running service on a platform that can run bm tunnel, or use your database provider's HTTP API through the HTTPS proxy. Connection setup costs more with a proxy hop: open the client once per function instance, not per request.
- MongoDB Atlas: add both Fixedmark IPs to the project's IP Access List. See MongoDB Atlas allowlisting.
- AWS RDS, Cloud SQL, and self-hosted Postgres or MySQL: see Postgres and MySQL allowlists.
// npm install mongodb socks
import { MongoClient } from "mongodb";
// FIXEDMARK_SOCKS_URL=socks5h://APP_ID:TOKEN@mum.egress.fixedmark.com:1080
const socks = new URL(process.env.FIXEDMARK_SOCKS_URL);
const client = new MongoClient(process.env.MONGODB_URI, {
proxyHost: socks.hostname,
proxyPort: Number(socks.port),
proxyUsername: decodeURIComponent(socks.username),
proxyPassword: decodeURIComponent(socks.password),
});
await client.connect();import (
"context"
"net/url"
"os"
"github.com/jackc/pgx/v5/pgxpool"
"golang.org/x/net/proxy"
)
func connect(ctx context.Context) (*pgxpool.Pool, error) {
u, err := url.Parse(os.Getenv("FIXEDMARK_SOCKS_URL"))
if err != nil {
return nil, err
}
pw, _ := u.User.Password()
dialer, err := proxy.SOCKS5("tcp", u.Host,
&proxy.Auth{User: u.User.Username(), Password: pw}, proxy.Direct)
if err != nil {
return nil, err
}
cfg, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
if err != nil {
return nil, err
}
cfg.ConnConfig.DialFunc = dialer.(proxy.ContextDialer).DialContext
return pgxpool.NewWithConfig(ctx, cfg)
}Common errors and fixes
The variable is empty inside the function but set in the UI
Check its scope includes Functions and its deploy context matches the deploy you are testing (production, deploy preview, or branch). Redeploy after changes.
407 Proxy Authentication Required
The proxy did not get valid credentials. Check that the env var holds the full URL with APP_ID:TOKEN@, with no quotes or trailing newline. If the token contains @, :, or /, URL-encode it. Some clients drop credentials from the URL: pass them separately (Deno's basicAuth, axios proxy.auth) or switch to undici's ProxyAgent, which reads them from the URL.
TLS errors to the proxy: wrong version number, EPROTO, Proxy CONNECT aborted, or unable to connect to proxy
The client does not support an https:// proxy URL and is speaking plain HTTP to port 443. Upgrade it: curl 7.52+, urllib3 1.26+ (requests), httpx 0.26+, undici ProxyAgent, Go 1.10+. In Ruby, use Typhoeus: Net::HTTP and Faraday's default adapter cannot tunnel HTTPS through a TLS proxy. For older axios releases, set proxy: false and pass https-proxy-agent as httpsAgent. Java's built-in HttpClient cannot speak TLS to a proxy at all. If you cannot upgrade, use the SOCKS5 URL.
ETIMEDOUT or ECONNRESET to the partner, or a 403 from the partner
The proxy connected, but the destination dropped you. Usually only one IP of the pair is on the allowlist, or the partner has not applied the change yet. Add both IPs. If a destination allowlist is set on your Fixedmark token, check the host and port are on it.
The IP check prints the platform's IP, not a Fixedmark IP
The request went direct. Node's built-in fetch ignores HTTPS_PROXY unless NODE_USE_ENV_PROXY=1 is set (Node 22.21+ and 24+). Many SDKs ignore proxy env vars too. Pass the proxy to the client explicitly, as in the snippets above.
429 or 407 with an X-Proxy-Error: quota_exceeded header
Planned behavior when you pass your plan's soft limit. Check usage in the dashboard or raise the plan.
Database driver has no proxy option
node-postgres, mysql2, psycopg, and most ORMs cannot use an HTTP proxy. Use a SOCKS5-capable driver with FIXEDMARK_SOCKS_URL, or run bm tunnel where the platform allows a second process.
Gotchas on Netlify
Background Functions and Scheduled Functions use the same runtime, so the same code works in them.
- Build-time requests (during
netlify build) run on build infrastructure. Pass the proxy URL to build scripts separately if they call an allowlisted API. - Function environment variables share AWS Lambda's 4 KB total limit. A long proxy URL counts against it.
Related guides
- IntegrationVercelFixed egress IPs for Vercel Functions without the $100 per project add-on.
- IntegrationAWS LambdaA fixed IP for Lambda without VPC, subnets, and two NAT Gateways.
- Use casePartner API allowlistingGive Salesforce, SOAP vendors, and payment gateways one dedicated IP pair to allowlist, wherever your code runs.
- Use caseMongoDB AtlasReplace 0.0.0.0/0 in your Atlas IP access list with two dedicated IPs, using the Node driver's SOCKS5 support.
Frequently asked questions
Does Netlify offer static outbound IPs?
Only through Private Connectivity, an Enterprise add-on enabled by your account manager. It covers builds and serverless functions, not Edge Functions. There is no self-serve static outbound IP on other plans.
Do Netlify Edge Functions work with Fixedmark?
Not at launch. Edge Functions run on a Deno-based edge runtime with no documented proxy option. Use a regular Netlify Function for allowlisted calls.
Do scheduled and background functions work?
Yes. They run on the same runtime as regular Netlify Functions, so the undici ProxyAgent code works unchanged.
Fix your egress IP.
Fixedmark is in early access. Join the list to get a dedicated IP pair for Netlify when your region opens.