Skip to content

RESTful Statelessness & Caching Explained: Scale APIs Like a Pro

DodaTech Updated Jun 6, 2026 7 min read

Statelessness and caching are REST constraints that work together to create APIs handling millions of requests through horizontal scaling and response reuse.

What You’ll Learn

  • What statelessness means in REST and why it matters
  • How stateless design enables horizontal scaling
  • Cache-Control headers, ETags, and conditional requests
  • The difference between private and public caching
  • How Durga Antivirus Pro uses caching for threat intelligence

Why Statelessness and Caching Matter

Without statelessness, every API request would require the server to remember previous interactions — consuming memory, complicating scaling, and creating single points of failure. Without caching, every client would hit the server for the same data repeatedly, wasting bandwidth and processing power. DodaTech’s Durga Antivirus Pro handles millions of devices by keeping API servers stateless (request goes to any server in the pool) and aggressively caching virus definition lookups (one hour cache = 99% cache hit rate).

    flowchart LR
    A["Client Device"] -->|"Request + Token"| B["Load Balancer"]
    B --> C["API Server 1"]
    B --> D["API Server 2"]
    B --> E["API Server 3"]
    C --> F["Cache Layer\n(Redis/CDN)"]
    D --> F
    E --> F
    F --> G["Database"]
    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#dcfce7,stroke:#16a34a
    style D fill:#dcfce7,stroke:#16a34a
    style E fill:#dcfce7,stroke:#16a34a
    style F fill:#fce7f3,stroke:#ec4899
  
Prerequisites: Familiarity with REST, RESTful Methods, and RESTful Messages.

What is Statelessness?

Statelessness means the server does not store any client context between requests. Every request from a client contains all the information needed to process it.

Stateful (wrong for REST):

// Server stores session in memory
app.post('/login', (req, res) => {
  sessions[req.body.token] = { user: 'admin', lastAccess: Date.now() };
  // Now future requests look up the session
});

app.get('/api/data', (req, res) => {
  const session = sessions[req.headers.token];
  // Fails if request goes to a different server!
});

Stateless (RESTful):

// Each request carries its own authentication
app.get('/api/v1/threats', (req, res) => {
  const token = req.headers.authorization?.split(' ')[1];
  const user = verifyJWT(token); // Self-contained token
  // No session lookup needed — token has all info
});

You might be wondering: “If the server doesn’t remember me, do I have to log in on every request?” No — you send an authentication token (like JWT) with every request. The token contains the user’s identity and permissions, cryptographically signed so the server can verify it without storing anything.

Why Statelessness Enables Scaling

Imagine an API server with 10,000 concurrent users. If each user’s session takes 1KB of memory, that’s 10MB per server.

  • Stateful: You need sticky sessions (same client → same server). Adding a 4th server doesn’t help if one server has too many sticky clients.
  • Stateless: Any server handles any request. Add 10 servers — each immediately handles any request. Your load balancer doesn’t need session affinity.

Real-world impact: Durga Antivirus Pro serves 5 million devices. With stateless design, traffic spikes during malware outbreaks are handled by auto-scaling groups that spin up new servers in seconds — no session synchronization needed.

Caching in REST

Caching stores responses and reuses them for identical future requests. REST requires responses to explicitly state whether they are cacheable.

Cache-Control Header

The Cache-Control header tells caches (browser, CDN, proxy) how long to keep a response:

// Cache for 1 hour — public proxies can cache too
Cache-Control: public, max-age=3600

// Cache for 10 minutes — only the client should cache
Cache-Control: private, max-age=600

// Don't cache at all
Cache-Control: no-store, no-cache, must-revalidate

Public means any cache (including shared CDN caches) can store the response. Private means only the browser (not shared caches) should store it — useful for user-specific data.

ETags and Conditional Requests

ETags are unique identifiers for a resource version. Clients send them in If-None-Match to ask “has this changed?”

// First request
GET /api/v1/virus-definitions HTTP/1.1

// Response
HTTP/1.1 200 OK
ETag: "v2.5.20260606"
Cache-Control: public, max-age=3600

// Second request (after cache expires)
GET /api/v1/virus-definitions HTTP/1.1
If-None-Match: "v2.5.20260606"

// Response — nothing changed
HTTP/1.1 304 Not Modified
// No body — client uses cached version

Why conditional requests matter: Durga Antivirus Pro’s virus definition file is 50MB. Without conditional requests, every device downloads 50MB every hour. With ETags, devices send a tiny conditional request, get a 304 response (empty body), and know their cached definitions are current.

Expires Header

A simpler alternative (less flexible than Cache-Control):

Expires: Sun, 07 Jun 2026 12:00:00 GMT

Common Mistakes

1. Storing Session State on the Server

Server-side sessions (req.session in Express, $_SESSION in PHP) break statelessness. Use JWT tokens that carry all necessary information, verified by cryptographic signature.

2. Not Setting Cache Headers

Without explicit cache headers, different caches behave differently — some cache aggressively, others never cache. Always set Cache-Control explicitly.

3. Caching Authenticated Responses as Public

A response containing user-specific data (GET /users/me/orders) should use Cache-Control: private. If marked public, a CDN might serve one user’s orders to another user.

4. Setting max-age Too High for Dynamic Data

Cache durations depend on how often data changes. Virus definitions change hourly — max-age=3600 is safe. The user’s antivirus scan history changes every second — max-age=0 or no-store are appropriate.

5. Ignoring Cache Invalidation

When you update a resource, clients with cached responses need to know. ETags solve this — the next conditional request gets the new ETag and fresh data. Some APIs also use webhooks or Cache-Tag headers for proactive invalidation.

Practice Questions

  1. What does statelessness mean in REST, and why is it required?
  2. How does statelessness enable horizontal scaling?
  3. What is the difference between Cache-Control: public and Cache-Control: private?
  4. How do ETags and conditional requests reduce bandwidth?
  5. Why are server-side sessions incompatible with RESTful design?

Answers:

  1. Each request must contain all context. No server-side session storage. It’s required because REST’s layered system constraint depends on each request being independently processable.
  2. Any server can handle any request because there’s no local session state. Load balancers don’t need sticky sessions, and auto-scaling groups can add/remove servers freely.
  3. public allows any cache (including CDNs and proxies) to store the response. private restricts caching to the client browser only, preventing shared caches from storing user-specific data.
  4. Clients store the response and send the ETag in future requests. If the resource hasn’t changed, the server returns 304 (no body). Instead of downloading the full resource, the client confirms its cached copy is current.
  5. Server-side sessions require the server to remember state between requests. This means the same client must hit the same server (sticky sessions), preventing horizontal scaling and violating statelessness.

Challenge: Design a caching strategy for Durga Antivirus Pro’s threat intelligence API. What endpoints should be cacheable? What Cache-Control values should each have? How would you handle cache invalidation when a new threat is discovered?

FAQ

Can a REST API use cookies for authentication?
: Technically yes, but it’s not recommended. Cookies introduce server-side state (the session is stored or referenced by the cookie), which breaks statelessness. JWT tokens in the Authorization header are preferred because the token itself contains all necessary state.
How do I invalidate a cached response?
: Option 1: Change the resource URI (new version number in URL). Option 2: Use ETags — clients send conditional requests and get fresh data when the ETag changes. Option 3: Use Cache-Tag headers with surrogate-key based invalidation through a CDN.
What happens if a cache is full?
: Caches use eviction policies (LRU — Least Recently Used) to remove old entries when full. That’s why max-age is important — it tells the cache how long to keep an entry before considering it stale.
Is statelessness required for every REST API?
: According to Fielding’s dissertation, yes — statelessness is one of the six constraints. In practice, some “RESTful” APIs use cookies or server-side sessions for convenience. However, doing so sacrifices the scalability benefits of true REST.

Try It Yourself

Test conditional requests with curl:

# First request — get the ETag
curl -i https://jsonplaceholder.typicode.com/posts/1

# Second request — use the ETag
ETAG='"abc123"'  # Replace with actual ETag from first response
curl -i -H "If-None-Match: $ETAG" https://jsonplaceholder.typicode.com/posts/1

If the resource hasn’t changed, you’ll get a 304 Not Modified with no body. If it has changed, you’ll get 200 OK with fresh data. This is the magic of conditional requests.

What’s Next

TopicDescription
REST API SecurityAuthentication, authorization, rate limiting
RESTful APIs OverviewReview the full REST architecture
GraphQL vs RESTCompare approaches to scaling and caching
HTTP Protocol GuideDeep dive into the protocol that powers it all

What’s Next

Congratulations on completing this Restful Statelessness Caching tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro