Firebase on the Server: Node, Python, Django and Laravel
Server-side Firebase uses a different SDK with different rules — literally. It bypasses them entirely, which is powerful and easy to misuse.
9 min read · updated August 5, 2026
Firebase has two families of SDK, and confusing them is the source of most server-side Firebase problems.
| Client SDK | Admin SDK | |
|---|---|---|
| Runs in | Browsers, phones | Your server, Cloud Functions |
| Signs in as | A user | The project itself |
| Security rules | Enforced on every call | Ignored completely |
| Credentials | Public config, safe to ship | A service account key — a real secret |
| Can do | What the rules allow | Anything |
What the Admin SDK is for
Four jobs, mainly:
- Verifying ID tokens. Your app sends a token; your server checks it is genuine before trusting who the user claims to be.
- Privileged writes. Updating data the user must not be able to change directly — order status, credit balances, roles.
- Custom claims. Attaching roles to users, which then appear inside their ID token and can be checked in security rules.
- Bulk work. Migrations, exports, scheduled cleanups — anything that would be far too many operations for a client.
Verifying a token — the pattern that matters most
If your server takes a user ID from the request body, anyone can send any user ID. The correct pattern is to send the ID token and verify it.
import { initializeApp, cert } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";
initializeApp({ credential: cert(serviceAccount) });
export async function requireUser(req) {
const header = req.headers.authorization ?? "";
const token = header.replace("Bearer ", "");
// Throws if the token is forged, expired, or from another project.
const decoded = await getAuth().verifyIdToken(token);
return decoded.uid; // Now you know who this is, for certain.
}verifyIdToken checks the signature against Google's public keys, the expiry, and that the token belongs to your project. Only after it returns do you know who is asking.
Custom claims: roles that reach your rules
Custom claims are small pieces of data baked into a user's ID token. They are the standard way to do roles.
// On your server — never from a client.
await getAuth().setCustomUserClaims(uid, { admin: true });The claim then appears in security rules automatically:
match /invoices/{id} {
allow read: if request.auth.token.admin == true;
}Python
There is an official Admin SDK for Python, and it works the same way as the Node one.
import firebase_admin
from firebase_admin import credentials, firestore, auth
cred = credentials.Certificate("service-account.json")
firebase_admin.initialize_app(cred)
db = firestore.client()
# Verify a token from your mobile app
decoded = auth.verify_id_token(id_token)
uid = decoded["uid"]
# Privileged write — no security rules apply here
db.collection("orders").document(order_id).update({"status": "shipped"})You may also come across Pyrebase. It is a third-party library that wraps the REST API and behaves like a *client*, respecting security rules. It is not the Admin SDK and is not maintained by Google. For server work, use firebase-admin.
Django
There is no Firebase database backend for Django's ORM, and there will not be — Django's ORM expects SQL. What people usually mean by "Django with Firebase" is one of two things:
- Firebase for authentication, Django for everything else. Your frontend signs in with Firebase, sends the ID token to Django, and Django verifies it with the Admin SDK and looks up its own user record. This is a good architecture and quite common.
- Django reading and writing Firestore directly through
firebase_admin, alongside or instead of its own database. Workable, but you lose the ORM, migrations and the admin site for that data.
The first is almost always the better idea.
Laravel and PHP
Google does not publish a PHP Admin SDK. The community library kreait/laravel-firebase is the standard answer and is well maintained.
The same architecture applies: Firebase handles sign-in, Laravel verifies the ID token and keeps its own data in MySQL or Postgres. Using Firestore as Laravel's main database means giving up Eloquent, which is usually a bad trade.
Keeping the service account key safe
- Never commit it. Add the JSON file to
.gitignorebefore you download it, not after. - Use environment variables in production. Most hosts let you paste the JSON into a secret. Read it from the environment rather than shipping a file.
- On Google Cloud, use nothing at all. Cloud Functions, Cloud Run and App Engine provide credentials automatically — call
initializeApp()with no arguments and it works. - Rotate if exposed. A leaked key is a full database compromise. Revoke it in the Google Cloud console and issue a new one; do not wait to see if anyone noticed.
// Inside Cloud Functions — no key needed, credentials are ambient.
import { initializeApp } from "firebase-admin/app";
initializeApp();Frequently asked questions
- What is the difference between the Firebase client SDK and the Admin SDK?
- The client SDK runs in browsers and apps, signs in as a user, and obeys your security rules. The Admin SDK runs on a trusted server, authenticates as the project itself, and ignores security rules entirely.
- Does the Firebase Admin SDK respect security rules?
- No. It bypasses them completely by design, because it is meant to run on servers you control. This is why a leaked service account key exposes your whole database.
- Can I use Firebase with Python?
- Yes, through the official `firebase-admin` package, which supports Firestore, Authentication, Storage and messaging. Pyrebase is a separate third-party library that behaves like a client rather than an admin SDK.
- Can I use Firebase as a Django database?
- Not through Django's ORM, which requires SQL. The usual approach is Firebase for authentication and Django with its own database for everything else, with Django verifying Firebase ID tokens.
- How do I verify a Firebase ID token on my server?
- Send the token in the Authorization header and call `verifyIdToken` in the Admin SDK. It checks the signature, expiry and project, and returns the user's ID. Never trust a user ID sent directly in a request body.
- Why has my custom claim not taken effect?
- Claims are embedded in the ID token when it is issued, so a change only applies after the token refreshes — up to an hour later. Force a token refresh on the client if it needs to apply immediately.