Error Medic

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.

Last updated:
Last verified:
2,109 words
Key Takeaways
  • 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.
Fix Approaches Compared
MethodWhen to UseTime to ApplyRisk
Force-refresh ID token (getIdToken(true))firebase 401 / firebase token expired on client SDK< 5 minutesLow — transparent to users
Rotate service account key in GCP IAMfirebase 401 / firebase unauthorized on server-side Admin SDK10–15 minutesMedium — old key must be decommissioned immediately
Rewrite Security Rulesfirebase 403 / firebase connection refused due to overly-strict rules15–30 minutesMedium — test with Rules Simulator before deploying
Exponential backoff + jitterfirebase 429 / firebase rate limited / firebase 502 / firebase 50330–60 minutes (code change)Low — standard reliability pattern
Increase Firebase quota via GCP consolePersistent firebase rate limit on Firestore or Auth1–2 business days (billing review)Low — may incur cost
Enable Firebase App CheckAbuse-driven rate limiting from unauthenticated callers1–2 hoursMedium — requires app attestation integration
Shard Firestore writes across documentsHotspot write contention causing rate limited on a single documentSeveral hours (refactor)Low — improves throughput permanently
Regional failover / multi-region projectRepeated firebase 502 / firebase 503 during regional outagesDays (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
  1. Open Firebase Console → Project → Usage and billing.
  2. 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).
  3. Under Authentication, check sign-in attempts — Firebase enforces per-IP and per-project thresholds to prevent credential stuffing.
  4. 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:

  1. Check DNS resolution: nslookup firestore.googleapis.com — should return multiple Google IPs.
  2. Check TLS: openssl s_client -connect firestore.googleapis.com:443 -brief — should complete the handshake.
  3. Check egress firewall rules — Firebase requires outbound TCP 443 to *.googleapis.com and *.firebaseio.com.
  4. Emulator conflict: if running Firebase Emulator locally, ensure FIREBASE_EMULATOR_HOST is 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

bash
#!/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 ==="
E

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

Related Guides