Google Maps API Keys — Setup, Security & Environment Guide¶
This guide shows how to create, configure, and secure two separate Google Maps API keys per environment for the Routes Planner feature:
- GOOGLE_MAPS_BROWSER_API_KEY — for front-end (public, used in browsers)
- GOOGLE_MAPS_SERVER_API_KEY — for back-end (private, used only by your server)
Environments:
- dev (local)
- stage (public staging www)
- prod (public production www)
Table of Contents¶
- Prerequisites
- Creating a Google Cloud Project
- Enabling Required APIs
- Key Strategy Overview
- Creating Keys (Per Environment)
- Securing Browser Keys (HTTP Referrers)
- Securing Server Keys (IP Addresses)
- Setting Up Billing
- Adding Keys to Your Application
- Monitoring Usage
- Troubleshooting
- Key Rotation
- Best Practices Summary
- Quick Reference
- Support
Prerequisites¶
- A Google account
- Access to Google Cloud Console
- Access to the mow-backend repository
- mow-backend code checked out and running locally
Creating a Google Cloud Project¶
-
Go to Google Cloud Console
-
Navigate to: https://console.cloud.google.com/
-
Create a New Project (or select an existing one)
-
Click the project dropdown at the top
- New Project → name it like
your-app-name-routes -
Create and wait a few seconds
-
Select Your Project
-
Ensure the new project is selected in the project dropdown
Enabling Required APIs¶
Enable these (minimum set for Maps JS and routing/geocoding):
- ✅ Maps JavaScript API (browser)
- ✅ Geocoding API (browser & server, depending on where you call)
- ✅ Directions API (browser & server, depending on where you call)
- ✅ Places API (optional but recommended; browser &/or server for autocomplete/details)
Tip: Only enable what you actually use. If you move geocoding/directions fully to the back end, you may disable them for the browser key and enable for the server key only.
Key Strategy Overview¶
We use two keys per environment to isolate risk and apply the tightest possible restrictions:
-
Browser key (public):
-
Visible in the page source and sent over the wire.
- Secured by HTTP referrer restrictions (domain whitelist) and API restrictions.
-
Used for Maps JavaScript API and any client-only features you intentionally allow.
-
Server key (secret):
-
Never exposed to browsers.
- Secured by IP address restrictions (stage/prod) and API restrictions.
- Used by your back end for server-to-server calls (e.g., geocoding, directions, place details if done server-side).
Recommendation: Prefer doing sensitive/expensive requests server-side (using the server key). Keep the browser key’s access minimal.
Creating Keys (Per Environment)¶
For each environment (dev, stage, prod), create two keys:
mow.apps.admin_portal.routes.dev.browsermow.apps.admin_portal.routes.dev.servermow.apps.admin_portal.routes.stage.browsermow.apps.admin_portal.routes.stage.servermow.apps.admin_portal.routes.prod.browsermow.apps.admin_portal.routes.prod.server
Steps (repeat for each key you create):
- APIs & Services → Credentials → + CREATE CREDENTIALS → API key
- Copy the key that’s generated.
- Click RESTRICT KEY (do not leave keys unrestricted).
- Apply the appropriate Application Restrictions and API Restrictions (see next sections).
- Save.
Securing Browser Keys (HTTP Referrers)¶
Application restriction: HTTP referrers (web sites)
API restrictions: Restrict to only what the browser must call directly (usually Maps JavaScript API; optionally Places if used in the browser). Avoid enabling Geocoding/Directions on the browser key if you can handle those on the server.
Allowed referrers (examples)¶
DEV (local) — add both:
http://localhost:8000/*
http://127.0.0.1:8000/*
STAGE (public staging) — replace with your real domains:
https://stage.yourdomain.com/*
https://www-stage.yourdomain.com/*
PROD (production) — replace with your real domains:
https://yourdomain.com/*
https://www.yourdomain.com/*
https://admin.yourdomain.com/*
Notes:
- Use
https://for public stage/prod; do not includehttp://for production. - Keep
/*at the end of each origin to cover all paths. - Do not include localhost in stage/prod keys.
Securing Server Keys (IP Addresses)¶
Application restriction: IP addresses (for stage/prod). The server key should never be used from a browser.
- STAGE / PROD: Add static egress IPs of your servers (load balancers, NAT gateways, or outbound proxies). If you use GCP, this is typically your instance/Cloud Run/Cloud Functions egress IP or your VPC NAT IP.
-
DEV (local): Options:
-
If your back end runs on your laptop: add your current public IP (find via your provider). Revisit when your IP changes.
- If using Docker or dynamic egress, keeping an IP list may be impractical. In dev only, you may temporarily set Application restriction = None and rely on API restrictions + quotas. Do not do this for stage/prod.
API restrictions (server key): Only allow the server-side APIs you actually call, e.g.:
- ✅ Geocoding API
- ✅ Directions API
- ✅ Places API (if using Details, Text Search, or Autocomplete server-side)
- ❌ Exclude Maps JavaScript API on the server key (it’s not needed server-side)
Setting Up Billing¶
Google Maps requires billing even with free credits. Typical free tier suffices for development/small production. Create budgets & alerts and consider per-key quotas (see Monitoring) so a leaked browser key cannot blow up spend.
Adding Keys to Your Application¶
Environment variables¶
Use two variables everywhere:
GOOGLE_MAPS_BROWSER_API_KEY=your_browser_key_here
GOOGLE_MAPS_SERVER_API_KEY=your_server_key_here
Maintain separate values per environment (dev, stage, prod).
.env (dev)¶
<project_root>/.env
# Google Maps API keys
GOOGLE_MAPS_BROWSER_API_KEY=your_dev_browser_key
GOOGLE_MAPS_SERVER_API_KEY=your_dev_server_key
Ensure
.envis in.gitignore. Never commit keys.
Docker Compose (base)¶
environment:
GOOGLE_MAPS_BROWSER_API_KEY: ${GOOGLE_MAPS_BROWSER_API_KEY}
GOOGLE_MAPS_SERVER_API_KEY: ${GOOGLE_MAPS_SERVER_API_KEY}
Application usage¶
- Front end (templates/env injection): use
GOOGLE_MAPS_BROWSER_API_KEYwhen loading the Maps JS script. - Back end (server-to-server calls): use
GOOGLE_MAPS_SERVER_API_KEYwhen calling Geocoding/Directions/Places endpoints from Python/Django.
Verifying keys are loaded (Django shell)¶
cd <project_root>
make dj-shell
python manage.py shell
Then inside the Django shell:
from mow.env import GOOGLE_MAPS_BROWSER_API_KEY, GOOGLE_MAPS_SERVER_API_KEY
print(f"Browser key prefix: {GOOGLE_MAPS_BROWSER_API_KEY[:10]}...")
print(f"Server key prefix: {GOOGLE_MAPS_SERVER_API_KEY[:10]}...")
print(len(GOOGLE_MAPS_BROWSER_API_KEY), len(GOOGLE_MAPS_SERVER_API_KEY))
You should see non-empty values and lengths ~39 characters.
Production / Staging¶
- Configure both env vars in your secrets manager or deploy platform.
- Use distinct keys for stage vs. prod, with correct referrer/IP restrictions per environment.
Monitoring Usage¶
- APIs & Services → Dashboard: view per-API metrics (requests, errors, latency).
- Billing → Budgets & alerts: set a monthly budget (e.g., $50) with alerts at 50%, 90%, and 100%.
Troubleshooting¶
“This page can’t load Google Maps correctly”¶
- Billing not enabled → enable billing.
- API not enabled → ensure required APIs are enabled.
- Wrong key used → browser is using the server key (don’t!). Load Maps JS with the browser key only.
- Referrer mismatch → add/adjust allowed referrers on the browser key.
RefererNotAllowedMapError¶
- Your current domain is not in the browser key HTTP referrer list.
- Add the correct domain(s) with
/*, save, wait ~5 minutes.
ApiNotActivatedMapError¶
- One of the required APIs is not enabled for the project. Enable the missing API.
Maps not loading on localhost (dev)¶
Add both to the browser key referrers:
http://localhost:8000/*
http://127.0.0.1:8000/*
Server calls failing (403) from stage/prod¶
- Check the server key IP restriction list. Ensure the egress IP(s) of your servers/load balancer/NAT are listed.
- Confirm you’re not accidentally using the browser key on the server.
Browser console shows API key¶
This is normal for the browser key. Security is via:
- HTTP referrer restrictions (domain whitelist)
- API restrictions (only Maps JS/limited)
- Usage quotas/per-key quotas
Key Rotation¶
Rotate if exposed, staff changes, suspected compromise, or every 6–12 months.
- Create a new key (apply same restrictions).
- Update environment variables (browser & server) in the target environment.
- Deploy and verify functionality.
- Delete the old key.
- Monitor for lingering requests using the old key.
Rotate browser and server keys independently if only one is affected.
Best Practices Summary¶
✅ DO
- Maintain separate browser/server keys per environment (dev/stage/prod)
- Use HTTP referrer restrictions for browser keys
- Use IP address restrictions for server keys (stage/prod)
- Restrict keys to only necessary APIs
- Enable billing, budgets, and per-key quotas
- Monitor usage regularly
- Rotate keys periodically
- Store keys in environment variables / secret stores
❌ DON’T
- Expose the server key to the browser
- Commit keys to version control or share in chat/email
- Reuse the same key across environments
- Leave keys unrestricted
- Ignore unusual usage spikes
Quick Reference¶
Required APIs
- Maps JavaScript API (browser key)
- Geocoding API (server key; browser only if required)
- Directions API (server key; browser only if required)
- Places API (optional, browser and/or server depending on usage)
Environment variables
GOOGLE_MAPS_BROWSER_API_KEY=...
GOOGLE_MAPS_SERVER_API_KEY=...
Referrer patterns (browser key)
- Dev:
http://localhost:8000/*,http://127.0.0.1:8000/* - Stage/Prod:
https://<your-domains>/*(no http)
Server key application restriction
- Stage/Prod:
IP addresses→ add server/load balancer/NAT egress IPs - Dev: Prefer IP restriction to your public IP; if impractical, use
Noneonly in dev with strict API limits
Support¶
- Google Maps Platform docs
- Google Cloud Console → APIs & Services → Dashboard (errors)
- Browser console for Maps JS errors
- This guide’s Troubleshooting section