How to Secure API Keys in React

Securing API keys in a React application is critical to protecting your external services from unauthorized access and unexpected usage costs. Because React is a client-side framework that runs entirely in the user’s browser, any code or environment variables shipped with the build can be easily extracted by inspect tools. This article explains why standard frontend environment variables are not secure for sensitive secrets, how to implement a backend proxy to keep keys hidden, and how to properly restrict public keys when exposure is unavoidable.

The Frontend Security Reality

The most important concept to understand is that you cannot completely hide secrets in front-end React code.

Many developers use .env files with variables prefixed with REACT_APP_ (for Create React App) or VITE_ (for Vite) and assume they are secure. However, during the build process, these variables are statically injected into your JavaScript files as plain text. Anyone who visits your website can open the browser developer tools, inspect your bundled source files, and find your keys.

Therefore, front-end environment variables should only be used to change configurations between environments (such as development, staging, and production URLs), not to store private API keys.

The Solution: Implement a Backend Proxy

The only secure way to use private API keys is to keep them on a server. Instead of your React application calling a third-party API directly, your React application calls your own backend server, which then makes the request to the third-party API.

1. Create a Server or Serverless Function

You can set up a simple Node.js/Express server or use serverless functions (such as AWS Lambda, Vercel Functions, or Netlify Functions) to act as a proxy.

2. Store the Key on the Server

Store your API key in a .env file on your server. Because this code never runs in the browser, the key remains completely hidden from the public.

// Express server example (server.js)
const express = require('express');
const axios = require('axios');
require('dotenv').config();

const app = express();

app.get('/api/weather', async (req, res) => {
  try {
    // The API key is securely accessed from the server's environment
    const apiKey = process.env.WEATHER_API_KEY; 
    const response = await axios.get(`https://api.weather.com/v1/data?key=${apiKey}`);
    res.json(response.data);
  } catch (error) {
    res.status(500).send('Error fetching data');
  }
});

app.listen(5000, () => console.log('Server running on port 5000'));

3. Fetch from React

In your React components, point your API requests to your own proxy endpoint rather than the third-party service.

// React Component (App.jsx)
import { useEffect, useState } from 'react';

function App() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Fetching from your secure backend proxy instead of the third-party API
    fetch('/api/weather')
      .then((res) => res.json())
      .then((data) => setData(data));
  }, []);

  return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
}

How to Handle Public API Keys

Some third-party services (like Firebase, Google Maps, or Stripe) require public keys to be used directly in your frontend code. Because these keys must be exposed to the browser to function, you must secure them using restrictions: