Firebase Rate Limit & Auth Errors: Fix 401, 403, 429, 502, 503 and Token Issues
Diagnose and fix Firebase rate limit, 401 unauthorized, 403 forbidden, 502/503 errors, and token expiration. Step-by-step commands included.
- Firebase rate limiting (HTTP 429 / RESOURCE_EXHAUSTED) is most often caused by exceeding Firestore read/write quotas, Realtime Database connection limits, or Firebase Auth sign-in attempt thresholds — check the Firebase console Usage tab first.
- 401 Unauthorized and 403 Forbidden errors almost always trace to an expired or missing ID token, incorrect Security Rules, or a service-account key that has been revoked or has insufficient IAM roles.
- 502 Bad Gateway and 503 Service Unavailable responses indicate transient Firebase backend issues or regional outages — implement exponential backoff with jitter before any other fix.
- Token expiration (firebase invalid token / firebase token expired) requires refreshing the Firebase ID token client-side via currentUser.getIdToken(true) or rotating the service account key server-side.
- Quick fix summary: rotate/refresh credentials → verify Security Rules → add exponential backoff → monitor quotas in the Firebase console → enable App Check to block abusive traffic.
| Method | When to Use | Time to Apply | Risk |
|---|---|---|---|
| Force-refresh ID token (getIdToken(true)) | firebase 401 / firebase token expired on client SDK | < 5 minutes | Low — transparent to users |
| Rotate service account key in GCP IAM | firebase 401 / firebase unauthorized on server-side Admin SDK | 10–15 minutes | Medium — old key must be decommissioned immediately |
| Rewrite Security Rules | firebase 403 / firebase connection refused due to overly-strict rules | 15–30 minutes | Medium — test with Rules Simulator before deploying |
| Exponential backoff + jitter | firebase 429 / firebase rate limited / firebase 502 / firebase 503 | 30–60 minutes (code change) | Low — standard reliability pattern |
| Increase Firebase quota via GCP console | Persistent firebase rate limit on Firestore or Auth | 1–2 business days (billing review) | Low — may incur cost |
| Enable Firebase App Check | Abuse-driven rate limiting from unauthenticated callers | 1–2 hours | Medium — requires app attestation integration |
| Shard Firestore writes across documents | Hotspot write contention causing rate limited on a single document | Several hours (refactor) | Low — improves throughput permanently |
| Regional failover / multi-region project | Repeated firebase 502 / firebase 503 during regional outages | Days (architectural change) | High — requires data migration planning |
Understanding Firebase Rate Limit and Auth Errors
Firebase surfaces a family of HTTP and SDK-level errors that share overlapping root causes. Before reaching for a fix, identify the exact signal you are seeing:
- HTTP 429 / gRPC RESOURCE_EXHAUSTED — you have exceeded a Firebase or underlying Google Cloud quota.
- HTTP 401 / UNAUTHENTICATED — the request carried no credentials, an expired ID token, or a revoked service-account key.
- HTTP 403 / PERMISSION_DENIED — credentials are valid but Security Rules or IAM policies deny the operation.
- HTTP 502 / HTTP 503 — Firebase's backend is temporarily unavailable; a transient condition.
auth/id-token-expired,firebase invalid token— the JWT issued by Firebase Auth has passed its one-hour TTL without a refresh.auth/network-request-failed,firebase connection refused— a network layer problem between your client and Firebase endpoints.firebase authentication failed— a broad SDK message; usually wraps one of the above.
Step 1: Identify the Error Source
1a. Check the Firebase Console
- Open Firebase Console → Project → Usage and billing.
- Under Firestore, look for spikes in Reads, Writes, or Deletes approaching daily free-tier limits (50 k reads / 20 k writes / 20 k deletes per day on Spark).
- Under Authentication, check sign-in attempts — Firebase enforces per-IP and per-project thresholds to prevent credential stuffing.
- Under Realtime Database, verify concurrent connections (100 on Spark, 200 k on Blaze).
1b. Read the Raw Error in Your Logs
Firebase Admin SDK (Node.js) errors contain a code and message:
FirebaseError: [code=resource-exhausted]: Quota exceeded: FirestoreReads
FirebaseError: [code=unauthenticated]: Request had invalid authentication credentials.
FirebaseError: [code=permission-denied]: Missing or insufficient permissions.
Firebase client SDK errors surface on the rejected Promise or onError callback:
code: 'auth/id-token-expired'
code: 'auth/network-request-failed'
code: 'permission-denied'
Step 2: Fix firebase rate limit (HTTP 429 / RESOURCE_EXHAUSTED)
2a. Implement Exponential Backoff
Firebase client SDKs do not retry automatically on 429. Add a helper:
async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 5): Promise<T> {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn();
} catch (err: any) {
const isRetryable =
err.code === 'resource-exhausted' ||
err.code === 'unavailable' ||
[429, 502, 503].includes(err.status);
if (!isRetryable || attempt === maxAttempts - 1) throw err;
const delay = Math.min(1000 * 2 ** attempt + Math.random() * 200, 30000);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error('Unreachable');
}
2b. Shard Hot Documents
Firestore limits a single document to roughly 1 write/second sustained. If you are incrementing a counter:
// BAD: single document counter
await db.doc('stats/global').update({ views: FieldValue.increment(1) });
// GOOD: distributed counter with 10 shards
const shardId = Math.floor(Math.random() * 10);
await db.doc(`stats/global/shards/${shardId}`).update({ views: FieldValue.increment(1) });
2c. Request a Quota Increase
For persistent Blaze-plan projects: GCP Console → APIs & Services → Firebase APIs → Quotas — select the specific quota and click Edit Quotas.
Step 3: Fix firebase 401 / firebase unauthorized / firebase token expired
3a. Client-Side: Force-Refresh the ID Token
Firebase ID tokens expire after 3600 seconds. Force a refresh:
import { getAuth } from 'firebase/auth';
async function getFreshToken(): Promise<string> {
const user = getAuth().currentUser;
if (!user) throw new Error('No authenticated user');
// Pass true to force a network round-trip to refresh
return user.getIdToken(/* forceRefresh */ true);
}
Attach the fresh token to outgoing requests:
const token = await getFreshToken();
const res = await fetch('https://your-api.example.com/data', {
headers: { Authorization: `Bearer ${token}` },
});
3b. Server-Side Admin SDK: Rotate the Service Account Key
# List existing keys
gcloud iam service-accounts keys list \
--iam-account=firebase-adminsdk-XXXXX@PROJECT_ID.iam.gserviceaccount.com
# Create a new key
gcloud iam service-accounts keys create new-key.json \
--iam-account=firebase-adminsdk-XXXXX@PROJECT_ID.iam.gserviceaccount.com
# Invalidate the old key (replace KEY_ID)
gcloud iam service-accounts keys delete KEY_ID \
--iam-account=firebase-adminsdk-XXXXX@PROJECT_ID.iam.gserviceaccount.com
Update your environment:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/new-key.json
# Or in your CI/CD secret store:
kubectl create secret generic firebase-key --from-file=key.json=new-key.json --dry-run=client -o yaml | kubectl apply -f -
Step 4: Fix firebase 403 / PERMISSION_DENIED
4a. Test Security Rules in the Simulator
Firebase Console → Firestore → Rules → Rules Playground. Simulate the failing read/write with the exact UID and document path from your logs. Common mistakes:
// Overly restrictive — blocks all reads
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if false; // <--- THIS blocks everything
}
}
}
A correct authenticated-user read rule:
allow read: if request.auth != null && request.auth.uid == resource.data.ownerId;
4b. Verify IAM Roles (Admin SDK)
# Check roles on the service account
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--filter="bindings.members:serviceAccount:firebase-adminsdk*" \
--format="table(bindings.role)"
The Admin SDK service account needs at minimum roles/firebase.admin or the granular roles/datastore.user.
Step 5: Fix firebase 502 / firebase 503 / firebase connection refused
These errors indicate transient Firebase infrastructure issues. Verify at status.firebase.google.com first. If the incident is active, wait it out with backoff. If no incident is listed:
- Check DNS resolution:
nslookup firestore.googleapis.com— should return multiple Google IPs. - Check TLS:
openssl s_client -connect firestore.googleapis.com:443 -brief— should complete the handshake. - Check egress firewall rules — Firebase requires outbound TCP 443 to
*.googleapis.comand*.firebaseio.com. - Emulator conflict: if running Firebase Emulator locally, ensure
FIREBASE_EMULATOR_HOSTis not set in production environments.
Step 6: Enable App Check to Prevent Abuse-Driven Rate Limiting
If your project is public-facing and you are being rate limited by bots, enable App Check:
import { initializeApp } from 'firebase/app';
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
const app = initializeApp(firebaseConfig);
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider('YOUR_RECAPTCHA_SITE_KEY'),
isTokenAutoRefreshEnabled: true,
});
In Security Rules, enforce App Check:
allow write: if request.auth != null && request.app.token.valid;
Frequently Asked Questions
#!/usr/bin/env bash
# Firebase Rate Limit & Auth Error Diagnostic Script
# Usage: bash firebase-diagnose.sh YOUR_PROJECT_ID
PROJECT_ID="${1:-YOUR_PROJECT_ID}"
echo "=== 1. Check Firebase service status ==="
curl -s https://status.firebase.google.com/incidents.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
if not data:
print('No active incidents reported.')
else:
for i in data[:3]:
print(i.get('name','?'), '-', i.get('status','?'))
" 2>/dev/null || echo "Could not fetch status page; check https://status.firebase.google.com manually."
echo ""
echo "=== 2. DNS resolution for Firebase endpoints ==="
for HOST in firestore.googleapis.com identitytoolkit.googleapis.com firebase.googleapis.com; do
echo -n " $HOST -> "
nslookup "$HOST" 2>/dev/null | awk '/^Address: / {print $2}' | head -1 || echo "FAILED"
done
echo ""
echo "=== 3. TLS connectivity (expect: Verification: OK) ==="
echo | openssl s_client -connect firestore.googleapis.com:443 -brief 2>&1 | grep -E 'Verification|error'
echo ""
echo "=== 4. HTTP status from Firestore REST API ==="
HTTP_CODE=$(curl -w '%{http_code}' -o /tmp/firebase_resp.json -s \
"https://firestore.googleapis.com/v1/projects/${PROJECT_ID}/databases/(default)/documents")
echo " HTTP $HTTP_CODE"
if [ "$HTTP_CODE" != "200" ]; then
echo " Response body:"
cat /tmp/firebase_resp.json | python3 -m json.tool 2>/dev/null || cat /tmp/firebase_resp.json
fi
echo ""
echo "=== 5. Check for emulator env vars (should be empty in production) ==="
for VAR in FIREBASE_EMULATOR_HOST FIRESTORE_EMULATOR_HOST FIREBASE_AUTH_EMULATOR_HOST; do
VAL=$(printenv "$VAR" 2>/dev/null)
if [ -n "$VAL" ]; then
echo " WARNING: $VAR=$VAL <-- unset this in production!"
else
echo " OK: $VAR is not set"
fi
done
echo ""
echo "=== 6. List service account keys (requires gcloud auth) ==="
if command -v gcloud &>/dev/null; then
SA=$(gcloud iam service-accounts list --project="$PROJECT_ID" \
--filter="displayName:firebase-adminsdk" \
--format="value(email)" 2>/dev/null | head -1)
if [ -n "$SA" ]; then
echo " Service account: $SA"
gcloud iam service-accounts keys list --iam-account="$SA" \
--project="$PROJECT_ID" --format="table(name.basename(),validAfterTime,validBeforeTime)" 2>/dev/null
else
echo " No firebase-adminsdk service account found or insufficient permissions."
fi
else
echo " gcloud not installed; skipping key check."
fi
echo ""
echo "=== Diagnostic complete ==="Error Medic Editorial
The Error Medic Editorial team is composed of senior DevOps engineers, SREs, and cloud architects with hands-on experience building and operating production systems on Firebase, Google Cloud, AWS, and Azure. We specialize in translating cryptic error codes into actionable fixes, drawing on real incident postmortems and official platform documentation.
Sources
- https://firebase.google.com/docs/firestore/quotas
- https://firebase.google.com/docs/auth/admin/errors
- https://firebase.google.com/docs/rules/simulator
- https://cloud.google.com/firestore/docs/best-practices#hotspots
- https://firebase.google.com/docs/app-check/web/recaptcha-provider
- https://stackoverflow.com/questions/46155/how-to-validate-an-email-address-in-javascript
- https://github.com/firebase/firebase-js-sdk/issues/4816
- https://cloud.google.com/apis/design/errors#handling_errors
- https://status.firebase.google.com