Serverless API security is the set of controls that decides who may call an HTTP Function, what a browser may send, how much traffic a client may generate, and what the function is allowed to access after a request is accepted.
Moving an API endpoint to serverless does not remove normal application-security responsibilities. The platform can manage the execution environment and scaling layer, but your application still needs authentication, authorization, request validation, CORS policy, abuse controls, scoped credentials, safe logs, and a clear trust boundary.
For small teams, the main risk is treating these controls as one problem. They solve different problems. Authentication proves identity. Authorization decides what that identity may do. CORS controls which browser origins may read responses. Rate limiting controls request pressure. Input validation decides whether the request is acceptable at all.
At Raff, we recommend separating those decisions before deploying an HTTP Function. A public webhook, a browser-facing API, and an internal service endpoint should not inherit the same security policy simply because they all use HTTPS.
Serverless API security starts with the trust boundary
Before choosing tokens, headers, or rate limits, define who is supposed to call the endpoint.
A useful classification is:
| Endpoint | Expected caller | Main controls |
|---|---|---|
| Public webhook | Known external provider | Signature/token validation, replay protection, idempotency |
| Browser API | Authenticated user in approved frontend | Authentication, authorization, CORS, validation, rate limiting |
| Public unauthenticated endpoint | Anyone | Strict validation, abuse controls, conservative rate limiting |
| Internal service endpoint | Known service identity | Service authentication, scoped authorization, network/app controls |
| Admin endpoint | Privileged staff/service | Strong authentication, authorization, narrow access, auditability |
This prevents a common mistake: protecting every endpoint only with one shared API key or one broad IP rule.
The trust boundary should answer:
- Who can reach the URL?
- Who should be accepted?
- What identity is available after authentication?
- What may that identity do?
- Which browser origins are expected?
- How much traffic should one identity generate?
- What downstream systems can the function access?
A public URL is not the same as a public API. The URL may be Internet-reachable while the application still requires a verified identity before doing useful work.
Authentication and authorization solve different problems
Authentication answers: Who is calling?
Authorization answers: What is that caller allowed to do?
A secure API normally needs both.
Examples of authentication inputs include:
- bearer tokens;
- signed session tokens;
- API keys;
- service credentials;
- webhook signatures or shared secrets;
- application-specific session cookies.
After the caller is authenticated, the handler should make an authorization decision using application context such as:
- user ID;
- tenant/account ID;
- role;
- API-key scope;
- resource ownership;
- action being requested.
A simplified path is:
HTTP request ↓ authenticate caller ↓ resolve identity ↓ authorize requested action/resource ↓ validate input ↓ perform business logic
Do not confuse possession of a credential with permission to perform every action. One API key should not automatically grant access to every tenant, bucket, database operation, or administrative endpoint.
For serverless functions, this separation is especially useful because functions are easy to expose independently. Each function or endpoint family can have a narrower security policy rather than inheriting one large application boundary.
CORS is a browser policy, not authentication
Cross-Origin Resource Sharing (CORS) controls whether browser JavaScript from one origin may read responses from another origin.
CORS does not prove who the user is. It does not stop command-line clients, backend services, bots, or attackers from sending HTTP requests directly.
That distinction is critical.
If your frontend runs at:
https://app.example.com
and calls:
https://api.example.com
then the API may need to permit that origin through CORS. But the endpoint should still authenticate and authorize the user separately.
A useful mental model is:
| Control | Question answered |
|---|---|
| CORS | May browser code from this origin read the response? |
| Authentication | Who is the caller? |
| Authorization | May this identity perform this action? |
| Rate limiting | How much traffic may this client/identity generate? |
| Validation | Is this request structurally and semantically acceptable? |
Do not use Access-Control-Allow-Origin: * as a shortcut on credentialed browser APIs unless the endpoint is intentionally public and the browser security model supports the intended flow.
Use the narrowest origin policy that matches the application. Development, staging, and production frontends may need separate allowed origins rather than one broad wildcard.
CORS can reduce unwanted browser access, but it is never a replacement for server-side authorization.
Rate limiting protects the function and its dependencies
Rate limiting controls how much request traffic a client, identity, tenant, token, or source may generate within a defined window.
For serverless APIs, that matters for two reasons:
- uncontrolled requests can increase function execution;
- fast function scaling can transfer pressure to databases, APIs, queues, and other dependencies.
The right enforcement key depends on the endpoint:
- unauthenticated endpoint → IP/source plus route may help;
- authenticated API → user, tenant, or API key is usually more meaningful;
- internal service → service identity;
- expensive operation → identity plus endpoint/action;
- webhook receiver → provider/account/event behavior rather than browser identity.
This guide does not own generic rate-limiter algorithms, quota headers, or detailed HTTP 429 design. Raff already has API Rate Limiting Explained, which covers fixed/sliding windows, token/leaky buckets, rate-limit keys, 429 responses, quotas, queueing, and user experience.
The serverless-specific point is architectural: place a limit before one caller can turn automatic scale-out into uncontrolled compute or downstream pressure.
A simple capacity chain is:
client traffic ↓ rate / abuse control ↓ function concurrency and scale ↓ database / API / storage capacity
Rate limiting and concurrency are related but different. Rate limiting controls incoming request behavior. Concurrency controls how much work executes at once. A production API may need both.
Request validation should happen before expensive work
Authentication does not make input safe.
Once identity is established, validate the request before the handler performs expensive or irreversible operations.
Validate dimensions such as:
- required fields;
- types and formats;
- maximum body size;
- allowed content types;
- enum/value ranges;
- resource identifiers;
- tenant/resource ownership;
- filenames and object keys;
- URLs or callback destinations;
- pagination and query bounds.
Reject invalid requests early rather than letting them consume database queries, storage operations, external API calls, or long-running CPU work.
This is both a security and cost-control measure. A malformed request that triggers expensive processing can be operationally harmful even when it is not a sophisticated attack.
For endpoints that start asynchronous work, validate and authorize before creating the job. Do not queue unauthorized or structurally invalid work and hope a later worker rejects it.
Webhook security needs signatures and replay awareness
Webhooks are a special case because the caller is usually another service rather than a human user.
A webhook should normally verify whatever sender-authentication mechanism the provider documents, such as:
- HMAC signature;
- signed timestamp;
- shared secret/token;
- provider-specific authorization header.
Where the provider supplies an event ID or timestamp, use it to reduce duplicate or replay risk.
The secure flow is:
receive raw request ↓ verify provider signature/token ↓ check timestamp/replay rule when available ↓ check duplicate event ID ↓ store durable event state ↓ acknowledge quickly
The existing Webhooks on Serverless Functions guide owns the detailed signature, idempotency, retry, replay, and durable-event architecture. This guide keeps webhook authentication in scope only to distinguish it from browser/user authentication.
Do not apply browser CORS thinking to webhook senders. Server-to-server webhook requests are not controlled by the browser's CORS policy.
Secrets and downstream credentials should stay scoped
An HTTP Function often needs access to more sensitive systems than the public caller should ever see directly.
Examples include:
- database credentials;
- object-storage keys;
- third-party API tokens;
- webhook secrets;
- encryption/signing keys;
- internal service credentials.
Keep those values out of source code and request-visible responses. Give each function only the access it actually needs.
A useful scope model is:
Public request ↓ Function identity / environment secrets ↓ Only required database / bucket / external API permissions
Raff Functions currently supports environment variables and masked secrets, plus scoped bindings to Raff Managed Databases and Raff Object Storage. Use those features to avoid putting broad credentials into code or sharing one credential across unrelated functions.
Scope also reduces incident impact. A file-processing function should not automatically have access to unrelated databases. A public webhook should not receive administrator-level infrastructure credentials just because another function needs them.
Logs should help investigations without leaking credentials
Security failures need enough context to investigate, but logs can create a second exposure path if they capture sensitive request data.
Useful security log fields include:
- request ID;
- endpoint/function name;
- authenticated user/service ID;
- tenant/account ID where safe;
- authorization result;
- validation failure category;
- HTTP status;
- rate-limit result;
- provider event ID for webhooks;
- duration;
- safe error code.
Avoid logging:
- passwords;
- bearer tokens;
- API keys;
- session cookies;
- full webhook secrets/signatures when unnecessary;
- database credentials;
- sensitive request bodies.
Raff Functions currently includes invocation logs and metrics, live log tail, 14-day log retention, invocation count, p50, p95, and error rate. Those signals can help correlate authentication failures, validation errors, traffic spikes, and function behavior without embedding secrets in log messages.
A good security log explains what happened and to which identity/resource, not the credential that proved it.
Serverless security controls should fail in the right order
The order of checks affects both security and cost.
For a typical authenticated API, a sensible order is:
TLS request arrives ↓ coarse abuse/request-size checks ↓ authentication ↓ authorization ↓ input validation ↓ business logic ↓ downstream data/API access
The exact order can vary. For example, webhook signature verification may need access to the raw request body before parsing it. Browser CORS preflight requests need their own response path. Some rate-limit keys cannot be determined until authentication resolves the user or tenant.
The principle is to reject cheap failures before expensive work when it is safe to do so.
A secure function should spend the least possible work on requests it will eventually reject.
This lowers the attack surface and reduces the financial impact of abusive or broken clients.
Raff HTTP Functions provide the runtime, not an automatic API security policy
Raff Functions currently provides HTTPS live URLs with automatic TLS, standard HTTP handlers, scale-to-zero, load-based autoscaling, editable runtime settings, logs and metrics, environment variables, masked secrets, and scoped database/object-storage bindings.
These product capabilities support secure API design, but they should not be interpreted as a claim that every application-level control is automatically configured for your endpoint.
Your application still needs to decide:
- authentication mechanism;
- authorization model;
- CORS origins and methods;
- validation rules;
- rate-limit or abuse-control layer;
- webhook verification behavior;
- secret scope;
- safe logging policy;
- downstream capacity limits.
Raff also has infrastructure-level security controls such as network isolation and platform security features, but those do not replace API-layer identity and authorization.
For public HTTP Functions, keep the security boundary explicit in application code or the appropriate gateway/middleware layer. Do not assume that because the platform manages the runtime, it also understands your tenant model, roles, subscription plans, or endpoint business cost.