Authentication Fundamentals: Securing Your Cloudflare Workers API
Learn to build secure authentication for your API using Cloudflare Workers. We cover password hashing, header validation, and implementing a robust login flow.

Previously in this course, we built The Dynamic Backend to store asset metadata in D1. Now, we need to ensure that only authorized users can interact with our sensitive API routes.
Authentication is the process of verifying who a user is. In a serverless environment like Cloudflare Workers, we don't have persistent sessions in the traditional sense, so we rely on stateless verification.
Principles of Secure Authentication
When building custom authentication for an API, you must adhere to three core pillars:
- Never store plain-text passwords: Always use a slow, salted cryptographic hash function (like
bcryptorArgon2). - Never send credentials in the URL: Credentials must be sent in the request body (during login) or headers (during subsequent requests).
- Use secure transmission: Always enforce HTTPS. If you followed our Configuring SSL/TLS Settings lesson, your origin is already protected.
Storing Hashed Passwords
We will use the Web Crypto API, which is built into the Cloudflare Workers runtime, to handle hashing. We never store the actual password; we store the result of a one-way function.
Here is how you generate a hash for a password using SHA-256 (for production, consider bcrypt via a library if you need salt handling, but this illustrates the primitive):
JAVASCRIPTasync function hashPassword(password) { const encoder = new TextEncoder(); const data = encoder.encode(password); const hash = await crypto.subtle.digest(CE9178">'SHA-256', data); return Array.from(new Uint8Array(hash)) .map(b => b.toString(16).padStart(2, CE9178">'0')) .join(CE9178">''); }
Implementing the Login Flow
Our login flow consists of two steps:
- Verification: The user sends a
POSTrequest with their email and password. We query D1 to find the user and compare the provided password hash against the stored hash. - Authorization: If they match, we return a unique token (typically a JWT). For this beginner level, we will use a simple custom header token system to validate requests.
Example: Validating a Request
We’ll create a middleware-style function that checks for an X-API-Key header.
JAVASCRIPTexport default { async fetch(request, env) { const apiKey = request.headers.get("X-API-Key"); // Simple validation against an environment variable if (apiKey !== env.SECRET_API_TOKEN) { return new Response("Unauthorized", { status: 401 }); } return new Response("Welcome to the secure zone!"); } }
Hands-on Exercise
- Update your D1 schema: Add a
userstable to your D1 database with columns foremailandpassword_hash. - Create a Registration Worker: Write a
POSTendpoint that accepts an email/password, hashes the password using thehashPasswordfunction above, and inserts it into D1. - Implement the Header Check: Add the snippet above to your existing API routes to protect them from unauthorized access.
Common Pitfalls
- Timing Attacks: Comparing hashes with a standard
==operator can be vulnerable to timing attacks. Always use a constant-time comparison function when checking sensitive strings. - Logging Credentials: Never
console.logthe request body or headers during authentication. This will leak credentials into your Cloudflare dashboard logs. - Storing Secrets in Code: Never hardcode your API tokens. We will cover how to manage these securely in a future lesson using
wrangler secret.
Frequently Asked Questions
Q: Why not use simple string comparison for passwords? A: If your database is ever leaked, an attacker would have every user's password instantly. Hashing ensures that even with the database file, they cannot recover the original passwords.
Q: Is SHA-256 enough for passwords?
A: It is a fast hash. For high-security systems, use a "key derivation function" like Argon2 or bcrypt, which are designed to be slow, making brute-force attacks significantly more expensive for attackers.
Q: What if I forget to set the header?
A: The server will return a 401 Unauthorized. This is the correct behavior for any API that requires authentication.
Recap
We’ve learned that security starts with never trusting the client, hashing sensitive data, and using headers for stateless validation. By integrating these practices into our Workers, we ensure our API remains a secure, private resource.
Up next: Rate Limiting Basics — we'll look at how to protect these endpoints from brute-force login attempts.
Work with me

Custom Email & File Storage System on Cloudflare (Google Workspace Alternative)
Your own private email + file storage suite on your domain — unlimited mailboxes, no per-seat fees. A self-owned Google Workspace alternative for a flat ~$5/month.

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.


