Stop writing API routes just to hide keys. SaltingIO bridge URLs let you secure third-party APIs in Next.js with CORS, rate limiting, and usage tracking — no backend needed.

You're building a Next.js app that calls OpenAI, Stripe, or a weather API. You've read the docs, you know NEXT_PUBLIC_ variables are exposed in the browser. So you create an API route in pages/api/ or app/api/ to hide the key.
Now you're maintaining a proxy endpoint, debugging CORS headers, adding rate limiting middleware, and deploying serverless functions. All to forward one API call.
There's a faster way. SaltingIO gives you a secure bridge URL that handles all of this --- in under 2 minutes.
Next.js has two types of environment variables:
NEXT_PUBLIC_* --- bundled into client-side JavaScript. Fully visible in the browser.If you need to call a third-party API from a client component (for streaming, real-time updates, or interactive UIs), you're stuck. Either you expose the key, or you build a backend proxy.
API routes solve this, but they come with baggage:
Every Next.js tutorial tells you the same thing: create a /api/proxy route, store your key in .env.local, and forward requests.
It works --- but it's overkill when all you need is a secure passthrough.
You end up writing boilerplate like this:
// app/api/chat/route.js
export async function POST(req) {
const body = await req.json();
const res = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
},
body: JSON.stringify(body)
});
return new Response(res.body, { headers: { 'Content-Type': 'application/json' } });
}
That's 15 lines of code, plus CORS handling, error handling, rate limiting, and deployment config. For a single API call.
SaltingIO replaces all of that with a single URL.
Here's how to secure any API key in your Next.js app using SaltingIO.
Go to your Secrets page in the SaltingIO dashboard. Click Add Secret and select Bridge as the type.
Paste the upstream API endpoint and add your credentials:
https://api.openai.com/v1/chat/completionsAuthorization: Bearer sk-your-secret-keySalting encrypts your key with AES-256-GCM before storing it. Zero-knowledge --- not even the Salting team can read it.
In your bridge settings, add your domain to the CORS Allowlist:
https://myapp.vercel.apphttp://localhost:3000 (for development)Now, even if someone finds your bridge URL, requests from unauthorized origins are rejected instantly.
Salting bridges come with built-in rate limiting. Configure the limits in your dashboard to prevent abuse --- no middleware required.
This is something API routes don't give you out of the box. With Salting, it's a toggle.
Drop the API route entirely. Call the Salting bridge URL directly from your client component.
// Client component calling your own API route
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gpt-4o',
messages: [{ role: 'user', content: prompt }]
})
});
// Client component calling the Salting bridge directly
const res = await fetch('https://api.salting.io/r/your-bridge-uuid', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gpt-4o',
messages: [{ role: 'user', content: prompt }]
})
});
const data = await res.json();
console.log(data.choices[0].message.content);
Same request body. Same response. But your API key never touches the browser, and you deleted an entire API route from your codebase.
Here's a complete React client component for Next.js that calls OpenAI through a Salting bridge:
'use client';
import { useState } from 'react';
const BRIDGE_URL = 'https://api.salting.io/r/your-bridge-uuid';
export default function Chat() {
const [input, setInput] = useState('');
const [reply, setReply] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const res = await fetch(BRIDGE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gpt-4o',
messages: [{ role: 'user', content: input }]
})
});
const data = await res.json();
setReply(data.choices[0].message.content);
setLoading(false);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Ask anything..."
/>
<button type="submit" disabled={loading}>
{loading ? 'Thinking...' : 'Send'}
</button>
</form>
{reply && <p>{reply}</p>}
</div>
);
}
No /api/chat route. No server-side code. No key in your environment. Just a secure bridge URL.
When your Next.js app calls the Salting bridge URL:
Salting checks the Origin header against your CORS allowlist. If the origin doesn't match, the request is rejected before anything else happens.
The request is checked against your configured rate limits. Abusive traffic is blocked automatically.
Salting decrypts your stored API key and injects it into the Authorization header server-side. The key never appears in the browser request.
The request is forwarded to the upstream API (e.g., OpenAI) with your credentials attached.
The API response is returned to your Next.js app. You can optionally use response transformation to extract only the fields you need using GJSON-style select queries.
Your key:
One advantage of routing requests through Salting: you get visibility.
The SaltingIO Dashboard shows request logs for your bridges, so you can track how your API is being used. Combine this with CORS enforcement and rate limiting, and you have a full security + observability layer without writing any infrastructure code.
You can also use the Playground tab to test your bridge in real-time --- send requests, inspect responses, and validate your setup before deploying.
If your API calls use dynamic parameters (like a city name or stock ticker), you don't need to hardcode them into the bridge.
Set up a template variable in your bridge URL:
https://api.weatherapi.com/v1/current.json?key=SECRET&q={{city}}
Then call it from Next.js with the value as a query parameter:
const res = await fetch('https://api.salting.io/r/your-bridge-uuid?city=London');
const data = await res.json();
Salting substitutes {{city}} with London before forwarding the request. Your key stays hidden, and your bridge stays flexible.
You don't need API routes, serverless functions, or a custom backend to secure API keys in Next.js.
With SaltingIO, you get:
Stop writing proxy routes. Start shipping secure apps.
Professional API security without the "Backend Tax."