Remote Site Settings and Named Credentials in Salesforce: Step-by-Step Configuration Guide
You’re building an Apex callout to an external REST API. You write the code, hit run, and Salesforce throws this error: “Unauthorized endpoint.”
Sound familiar?
Every Salesforce developer runs into this wall. Salesforce blocks outbound HTTP calls by default — it’s a deliberate security decision, not a bug. To make external callouts work, you need to tell Salesforce which endpoints are allowed and, in most cases, how to authenticate with them.
That’s where Remote Site Settings and Named Credentials come in. Both solve part of this problem, but they work differently, serve different purposes, and have real implications for how you manage deployments and credentials across orgs.
There’s also a third concept that trips people up: Connected Apps. And in newer orgs, you’ll encounter External Credentials, which adds another layer to the architecture.
This guide explains what each tool does, walks you through the exact configuration steps for both Remote Site Settings and Named Credentials, shows you how to wire them up in Apex, and covers the best practices that matter in production.
What is Remote Site Settings in Salesforce?
Remote Site Settings (RSS) is Salesforce’s endpoint whitelist. Before any Apex code can make an HTTP callout to an external URL, that URL must be registered in Setup → Security → Remote Site Settings. If it’s not there, Salesforce refuses the connection outright.
Salesforce runs in a multi-tenant environment. Unrestricted outbound requests could enable server-side request forgery (SSRF) attacks or let malicious packages exfiltrate data silently. Whitelisting forces a conscious, explicit decision about every endpoint your code touches.
RSS answers one question: “Is this endpoint allowed?” That’s all it does. It has no concept of authentication, token storage, or credential management.
Step-by-Step: Configuring Remote Site Settings
Here’s the exact path through Setup to register an external endpoint.
Step 1: Open Remote Site Settings

In Salesforce Setup, use the Quick Find box and type “Remote Site Settings”. Click it under Security.
Step 2: Click “New Remote Site”
You’ll land on the Remote Site Settings list page. Click the New Remote Site button in the top right.
Step 3: Fill in the Remote Site Details

The form asks for:
Remote Site Name A unique API name for this entry. No spaces — use underscores. Example: Weather_API_Prod
Remote Site URL The base URL of the external endpoint, including the protocol. Example: https://api.openweathermap.org
A few rules here:
- Include
https://orhttp://— don’t leave it off - Don’t include a trailing slash or path.
https://api.openweathermap.orgis correct;https://api.openweathermap.org/data/3.0/onecallis overly specific and can break callouts to other paths - If the API runs on a non-standard port, include it:
https://api.example.com:8443
Disable Protocol Security Leave this unchecked unless you’re working in a sandboxed dev environment and truly need HTTP (not HTTPS). Enabling it on a production org is a security risk.
Description Optional, but worth filling in. Future you — and your teammates — will thank present you for writing something like: “Production endpoint for Weather.io forecast API. Used by WeatherCalloutService class.”
Step 4: Save
Click Save. The endpoint is now whitelisted. Salesforce will accept outbound HTTP requests to any path under that base URL.
Step 5: Verify in the List
You’ll be returned to the Remote Site Settings list. Confirm your new entry appears with an Active checkbox ticked. If the checkbox is unticked, the whitelist entry is inactive and callouts will still fail.
[IMAGE: Screenshot placeholder of Salesforce Remote Site Settings configuration form, alt=”Salesforce Remote Site Settings configuration form fields example”]

Note: You can use this free weather forecasting api to play around – https://openweathermap.org/
How to Use Remote Site Settings in Apex
Once the endpoint is whitelisted, your Apex code can make HTTP callouts directly. Here’s a complete working example.
RSS doesn’t store credentials — you have to inject them yourself. Here’s how that typically looks with an API key:
Apex Code
public class WeatherService {
// API key stored in Custom Metadata (NOT hardcoded)
private static final String API_KEY =
WeatherAPI_Config__mdt.getInstance('Default').API_Key__c;
public static String getForecast(String city) {
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.openweathermap.org/data/2.5/weather?lat=44.34&lon=10.99&appid='+API_KEY);
req.setMethod('GET');
req.setHeader('Content-Type', 'application/json');
req.setTimeout(10000);
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
return res.getBody();
} else {
throw new CalloutException('API error ' + res.getStatusCode() + ': ' + res.getBody());
}
}
}
Notice the API key is pulled from Custom Metadata, not hardcoded. Hardcoding credentials in Apex is a serious security issue — they end up in version control and are visible to anyone with access to the code.

Note: You should also add the endpoint url to Custom Metadata instead of hardcoding, this allows you to use point to different endpoints for UAT and Prod
Mockcallout class for Test Class
Any Apex class that makes HTTP callouts needs a mock for unit tests. Salesforce won’t allow real callouts in test context.
Apex Code
// Mock class
@isTest
global class WeatherServiceMock implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setHeader('Content-Type', 'application/json');
res.setBody('{"forecast": "Sunny", "temp": 72}');
res.setStatusCode(200);
return res;
}
}
// Test class
@isTest
private class WeatherServiceTest {
@isTest
static void testGetForecast() {
Test.setMock(HttpCalloutMock.class, new WeatherServiceMock());
Test.startTest();
String result = WeatherService.getForecast('Austin');
Test.stopTest();
System.assertNotEquals(null, result);
System.assert(result.contains('forecast'));
}
}
What is Named Credentials in Salesforce?
Named Credentials solve both the whitelisting and authentication problems in one place. When you define a Named Credential, Salesforce automatically registers the endpoint (no separate RSS needed) and stores the authentication configuration securely — encrypted, off your hands.
For OAuth 2.0, Salesforce handles token requests, stores access tokens encrypted at rest, and automatically refreshes tokens when they expire. You don’t write any of that logic.
Named Credentials also make your Apex cleaner. Instead of a full URL, you reference the credential by name with a callout: prefix. When the endpoint URL changes, you update the Named Credential — not your code.
Step-by-Step: Configuring Named Credentials
The configuration steps depend on whether you’re using the legacy model (Named Credential only) or the new model (External Credential + Named Credential). For most straightforward integrations, the legacy model is simpler and still fully supported.
Legacy Named Credential (Simple Setup)
Step 1: Navigate to Named Credentials

In Setup, search for “Named Credentials” in Quick Find. Click it under Security.
Step 2: Click “New Named Credential” (Legacy)
On the Named Credentials list page, click New Named Credential. If you see options for “New” vs. “New Legacy”, choose New Legacy for the simpler single-object approach.
Step 3: Fill in the Named Credential Fields
Label A human-readable name. Example: Weather API Production
Name The API name your Apex code references. This auto-populates from the Label but you can edit it. Example: Weather_API_Prod. No spaces.
URL The base URL of the external API. Same rules as RSS: include the protocol, no trailing slash. Example: https://api.weather.io
Identity Type Controls whether the credential is shared across all users or per-user:
- Named Principal — one credential used for all callouts, regardless of who triggers them. Use this for service integrations.
- Per User — each user authenticates separately. Use this when the external system needs to know which Salesforce user is making the request.
Authentication Protocol Choose from:
- No Authentication — public APIs with no auth required
- Password — HTTP Basic Auth (username + password)
- OAuth 2.0 — the most common choice for modern APIs
- JWT — for JWT-based authentication flows
- AWS Signature Version 4 — for AWS services
For Password: Fill in the Username and Password fields directly. Salesforce encrypts and stores them.
For OAuth 2.0: You’ll need:
- Auth Provider — a Salesforce Auth Provider configured for this external service (create this first in Setup → Auth. Providers)
- Scope — the OAuth scopes your integration needs (e.g.,
read write) - Start Authentication Flow on Save — check this to immediately run through the OAuth flow and obtain tokens when you save
Generate Authorization Header Keep this checked for most use cases. It tells Salesforce to automatically inject the Authorization header with the appropriate credentials on every callout.
Allow Merge Fields in HTTP Body Optional. Enables Salesforce merge field syntax in request bodies sent from Flow or External Services.
Step 4: Save
Click Save. For OAuth 2.0 with “Start Authentication Flow on Save” checked, Salesforce will redirect you through the external provider’s login page to obtain the initial token.
Step 5: Confirm Authentication Status
After saving, the Named Credential detail page shows an Authentication Status field. For OAuth 2.0 Named Credentials with Named Principal identity type, it should show Authenticated. If it shows an error, the OAuth flow didn’t complete — check your Auth Provider configuration.
[IMAGE: Screenshot placeholder of Salesforce Named Credential configuration form, alt=”Salesforce Named Credentials configuration with OAuth 2.0 setup fields”]
How to Use Named Credentials in Apex
With Named Credentials configured, your Apex code becomes significantly simpler.
Basic GET Request Using Named Credentials
Apex Code
public class WeatherService {
public static String getForecast(String city) {
HttpRequest req = new HttpRequest();
// 'callout:Weather_API_Prod' references the Named Credential Name
req.setEndpoint('callout:Weather_API_Prod/v2/forecast?city=' +
EncodingUtil.urlEncode(city, 'UTF-8'));
req.setMethod('GET');
req.setTimeout(10000);
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
return res.getBody();
} else {
throw new CalloutException('API error ' + res.getStatusCode());
}
}
}
The callout:Weather_API_Prod prefix does three things automatically:
- Resolves to the full base URL configured in the Named Credential
- Injects the Authorization header with the correct credentials
- Handles OAuth token refresh if the token has expired
There’s no credential handling code in the Apex class at all.
POST Request with JSON Body
Apex Code
public class CRMSyncService {
public static String createContact(String firstName, String lastName, String email) {
Map<String, String> payload = new Map<String, String>{
'first_name' => firstName,
'last_name' => lastName,
'email' => email
};
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:External_CRM/api/v1/contacts');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(payload));
req.setTimeout(15000);
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 201) {
return res.getBody();
} else {
throw new CalloutException('Create failed: ' + res.getStatusCode() + ' ' + res.getBody());
}
}
}
Referencing Named Credentials in a Wrapper Class
For integrations with multiple endpoints, a wrapper class keeps things clean:
Apex Code
public class ExternalCRMClient {
private static final String BASE_CALLOUT = 'callout:External_CRM';
public static HttpResponse get(String path) {
return send('GET', path, null);
}
public static HttpResponse post(String path, Object body) {
return send('POST', path, JSON.serialize(body));
}
public static HttpResponse put(String path, Object body) {
return send('PUT', path, JSON.serialize(body));
}
private static HttpResponse send(String method, String path, String body) {
HttpRequest req = new HttpRequest();
req.setEndpoint(BASE_CALLOUT + path);
req.setMethod(method);
req.setHeader('Content-Type', 'application/json');
req.setHeader('Accept', 'application/json');
req.setTimeout(20000);
if (body != null) {
req.setBody(body);
}
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() >= 400) {
throw new CalloutException(
method + ' ' + path + ' failed with ' + res.getStatusCode() + ': ' + res.getBody()
);
}
return res;
}
}
Now every callout in your codebase goes through one method. When you need to add logging, retries, or error handling, there’s one place to do it.
Mocking Named Credential Callouts in Tests
The mock pattern works exactly the same way as with RSS:
Apex Code
@isTest
global class ExternalCRMMock implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setHeader('Content-Type', 'application/json');
if (req.getEndpoint().contains('/contacts') && req.getMethod() == 'POST') {
res.setBody('{"id": "cont_123", "status": "created"}');
res.setStatusCode(201);
} else {
res.setBody('{"error": "Not found"}');
res.setStatusCode(404);
}
return res;
}
}
@isTest
private class ExternalCRMClientTest {
@isTest
static void testPostContact() {
Test.setMock(HttpCalloutMock.class, new ExternalCRMMock());
Test.startTest();
HttpResponse res = ExternalCRMClient.post('/api/v1/contacts',
new Map<String, String>{'email' => 'test@example.com'});
Test.stopTest();
System.assertEquals(201, res.getStatusCode());
}
}
Named Credentials Best Practices
These come from working with real integration projects where credential management, deployment pipelines, and multi-org setups created real headaches. Each one prevents a specific class of problem.
1. Never Store Credentials in Apex Code or Custom Settings
This is the most important one. API keys in Apex end up in version control. Even if your repo is private today, that could change. Custom Settings are readable by anyone with object permissions. Named Credentials are the right place for credentials — they’re encrypted, auditable, and not exported with metadata.
If you’re currently using Custom Settings or Custom Metadata just to store a token: migrate it to a Named Credential.
2. Use Named Principal for Service Integrations, Per User for User-Context Integrations
Named Principal is the right choice when your integration acts as a service account — scheduled jobs, automated processes, or any flow where the “user” is really Salesforce itself.
Per User is right when the external system needs to enforce its own access control per Salesforce user. For example, a reporting system where each user should only see their own data.
Mixing these up leads to either security gaps (everyone sharing one token that has admin access) or confusing UX (users unexpectedly prompted to authenticate).
3. Store the Named Credential Name in Custom Metadata, Not Hardcoded in Apex
Instead of hardcoding callout:Weather_API_Prod in ten different Apex classes, store the credential name in a Custom Metadata record:
Apex Code
String credentialName = Integration_Config__mdt.getInstance('WeatherAPI').Named_Credential__c;
req.setEndpoint('callout:' + credentialName + '/v2/forecast');
When you need to point to a different credential (e.g., sandbox vs. production), you update one Custom Metadata record, not ten Apex files.
4. Use One Named Credential Per Environment (Sandbox vs. Production)
Don’t share Named Credentials across environments. A sandbox should point to a sandbox or staging endpoint; production should point to the real endpoint. Create:
Weather_API_Dev→https://sandbox.weather.ioWeather_API_Prod→https://api.weather.io
Then use environment-aware Custom Metadata to resolve the right one at runtime. This also prevents sandbox tests from accidentally hitting production systems.
5. Always Set a Timeout
Salesforce has a default callout timeout of 10 seconds, but you should set it explicitly:
Apex Code
req.setTimeout(10000); // milliseconds
The maximum is 120,000ms (2 minutes). For synchronous Apex triggered by a user action, 10–15 seconds is a reasonable ceiling. For async (Queueable, Batch, Future), you have more flexibility, but set a timeout anyway. Silent hangs are harder to debug than explicit timeouts.
6. Handle Non-2xx Responses Explicitly
Don’t just check for 200. APIs return 201 for creates, 204 for deletes, 400 for bad requests, 401 for auth failures, 429 for rate limits, 503 for downtime. A blanket “not 200 = error” approach hides useful information.
Apex Code
Integer statusCode = res.getStatusCode();
if (statusCode == 200 || statusCode == 201) {
// Success
} else if (statusCode == 401) {
throw new CalloutException('Authentication failed. Check Named Credential auth status.');
} else if (statusCode == 429) {
throw new CalloutException('Rate limit hit. Retry after delay.');
} else if (statusCode >= 500) {
throw new CalloutException('External service error: ' + statusCode);
} else {
throw new CalloutException('Unexpected response: ' + statusCode + ' ' + res.getBody());
}
7. Log Callout Metadata, Not Payloads
When something goes wrong at 2am, you want logs. But logging full request/response bodies can expose sensitive data and bloat your storage. Log the metadata:
Apex Code
System.debug(LoggingLevel.INFO, 'Callout to ' + req.getEndpoint() +'completed in [async] with status ' + res.getStatusCode());
For production integrations, write this to a custom object (`Integration_Log__c`) with fields for endpoint, status code, timestamp, and a truncated error message. Full payload logging should be opt-in, behind a debug flag in Custom Metadata.
8. Secrets Don’t Deploy — Document the Re-Entry Process
Named Credentials deploy via SFDX and change sets, but the secrets (client secrets, API keys) do not. After every deployment to a new org, someone needs to re-enter the credentials manually. This is by design — Salesforce won’t export encrypted secrets in metadata.
The practical fix: document exactly which Named Credentials need to be re-configured after deployment, what fields need values, and where those values come from (a secrets manager, a vault, a specific team member). Add it to your deployment runbook.
Remote Site Settings vs Named Credentials
| Feature | Remote Site Settings | Named Credentials |
|---|---|---|
| Primary purpose | Whitelist an endpoint | Whitelist + authenticate |
| Authentication support | ❌ None | ✅ Basic, OAuth 2.0, JWT, AWS, etc. |
| Token auto-refresh | ❌ Manual | ✅ Automatic |
| Credential storage | ❌ Not included | ✅ Encrypted by Salesforce |
| Deployable via metadata | ✅ Yes | ✅ Yes |
| Works with Apex callouts | ✅ Yes (manual auth) | ✅ Yes (auto auth) |
| Works in Flow | ❌ No native support | ✅ Yes (HTTP Callout actions) |
| Per-user authentication | ❌ No | ✅ Yes (with External Credentials) |
| Setup complexity | Low | Medium |
For production integrations involving any form of authentication, Named Credentials is the right default. RSS stays useful for public APIs, quick dev testing, and situations where a third-party package manages its own HTTP layer.
Named Credentials vs Connected App
**Named Credentials and Connected Apps solve different problems.** Named Credentials are for *Salesforce calling out* to an external system. Connected Apps are for *external systems calling into* Salesforce.
| Scenario | What to Use |
|---|---|
| Apex calling an external REST API | Named Credentials |
| Mobile app authenticating users via Salesforce | Connected App |
| Salesforce-to-Salesforce integration | Both |
| CI/CD deploying to Salesforce | Connected App (JWT Bearer) |
They overlap only when the external system you’re calling *is* Salesforce — in which case the outbound Named Credential configuration might reference a Connected App’s client ID and secret.
Named Credentials and External Credentials: The Modern Architecture
The legacy Named Credential bundled endpoint and auth into one object. The newer model separates them:
- External Credential — defines the auth protocol, stores the secrets
- Named Credential — defines the URL, points to an External Credential
- Permission Set mapping — controls who can use the credential
The modern call chain:
Permission Set → External Credential → Named Credential → Apex Callout
The key benefit: per-user authentication becomes manageable. Each user authenticates with their own token. When someone leaves the company, revoking their Permission Set access immediately cuts off their token — no code changes needed.
For simple service-account integrations, the legacy Named Credential model is still fine. Upgrade to External Credentials when you need per-user auth or a cleaner separation of concerns across your team.
Frequently Asked Questions
Do Named Credentials replace Remote Site Settings entirely? For most production use cases, yes. Named Credentials automatically handle endpoint whitelisting. You only still need RSS for hardcoded endpoint URLs in Apex that don’t use the callout: prefix.
Can I use Named Credentials in Salesforce Flow? Yes. Starting with Spring ’23, Salesforce supports HTTP Callout actions in Flow that leverage Named Credentials. Admins can build declarative API integrations without writing Apex.
What authentication types do Named Credentials support? Named Credentials support: No Authentication, Password (Basic Auth), OAuth 2.0 (authorization code, client credentials, JWT bearer), and AWS Signature Version 4. Through External Credentials, the list also includes custom authentication protocols.
When should I use a Connected App instead of Named Credentials? Use a Connected App when an external system needs to authenticate into Salesforce. Use Named Credentials when Salesforce needs to call out to an external system.
What is the difference between Named Credentials and External Credentials in Salesforce? Named Credentials define the endpoint URL and reference authentication configuration. External Credentials define the authentication protocol, secrets, and permission mappings. External Credentials are the auth layer; Named Credentials are the connection layer.
Are Named Credentials deployable via SFDX or Change Sets? Yes — but secrets are not exported for security reasons. After deployment, credentials must be manually re-entered in the target org.
What happens if a Named Credential’s OAuth token expires during a callout? Salesforce automatically attempts a token refresh before the callout fails. If the refresh also fails (e.g., the refresh token was revoked), the callout throws a CalloutException. Your error handling should catch this and alert the relevant admin.
Summary
Remote Site Settings — a blunt endpoint whitelist. Fast to configure, zero authentication support. Still useful for public APIs and quick dev unblocking.
Named Credentials — endpoint whitelisting plus secure, automated authentication. The right default for any production integration that requires credentials.
External Credentials — the modern architecture that separates auth config from endpoint config and enables per-user authentication via Permission Sets.
Connected Apps — for inbound authentication into Salesforce, not outbound.
The configuration steps for each are straightforward once you understand what each layer does. Get the credential management right from the start, and you’ll avoid the messy credential-rotation and deployment surprises that hit teams who hardcode tokens in Apex.