SSR Middleware — Authentication, Logging, and Request Processing
In this tutorial, you will learn about SSR Middleware. We cover key concepts, practical examples, and best practices to help you master this topic.
SSR middleware handles authentication checks, request logging, redirects, caching headers, bot detection, and request preprocessing for server-side rendered applications.
What You'll Learn
By the end of this tutorial, you will understand how middleware works in SSR applications, how to implement authentication middleware that redirects unauthenticated users, logging middleware for request tracking, caching middleware for performance, bot detection for SEO, and request preprocessing.
Why It Matters
Middleware runs on every SSR request. It is the first line of defense and the first opportunity to optimize performance. Well-structured middleware handles cross-cutting concerns (auth, logging, caching) without cluttering page components. SSR middleware can also redirect, rewrite, or block requests before they reach the rendering pipeline.
Real-World Use
A Next.js e-commerce site uses middleware to: redirect old product URLs to new ones (301), detect and block suspicious bot traffic, set cache headers based on user authentication status, log all requests for analytics, and check authentication for admin pages. This middleware handles 50,000 requests per minute without slowing down the SSR pipeline.
SSR Middleware Pipeline
┌──────────────────────────────────────────────────────────┐
│ SSR Middleware Pipeline │
├──────────────────────────────────────────────────────────┤
│ │
│ Request │
│ │ │
│ 1. Security Middleware │
│ • Rate limiting │
│ • IP blocking │
│ • Bot detection │
│ • CORS headers │
│ │
│ 2. Authentication Middleware │
│ • Session/cookie check │
│ • Redirect to login │
│ • Set user context │
│ │
│ 3. Logging Middleware │
│ • Request logging │
│ • Performance tracking │
│ │
│ 4. Caching Middleware │
│ • Set Cache-Control headers │
│ • Check Redis cache │
│ │
│ 5. SSR Rendering │
│ • Render page with user context │
│ │
└──────────────────────────────────────────────────────────┘
Think of SSR middleware like airport security checkpoints. Every passenger (request) goes through multiple stations: ID check (authentication), baggage scan (security), boarding pass verification (caching), and finally boarding the plane (SSR rendering). Each station handles a specific concern without interfering with others.
Next.js Middleware
// middleware.js — Runs before every request
import { NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
export async function middleware(request) {
const { pathname, searchParams } = request.nextUrl;
// 1. Redirect old URLs
if (pathname.startsWith('/old-products')) {
const newPath = pathname.replace('/old-products', '/products');
return NextResponse.redirect(new URL(newPath, request.url), 301);
}
// 2. Authentication check for admin routes
if (pathname.startsWith('/admin')) {
const token = await getToken({ req: request });
if (!token) {
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('callbackUrl', pathname);
return NextResponse.redirect(loginUrl);
}
if (token.role !== 'admin') {
return NextResponse.redirect(new URL('/403', request.url));
}
}
// 3. Set cache headers for public pages
const response = NextResponse.next();
if (pathname.startsWith('/products') || pathname.startsWith('/blog')) {
response.headers.set(
'Cache-Control',
'public, s-maxage=60, stale-while-revalidate=30'
);
}
// 4. Add security headers
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
// 5. Bot detection (simplified)
const userAgent = request.headers.get('user-agent') || '';
if (isBot(userAgent)) {
response.headers.set('X-Robots-Tag', 'noindex');
}
return response;
}
function isBot(userAgent) {
const bots = ['Googlebot', 'Bingbot', 'Slurp', 'DuckDuckBot', 'Baiduspider'];
return bots.some(bot => userAgent.includes(bot));
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|images).*)',
],
};
Custom Express Middleware for SSR
const express = require('express');
const { renderToString } = require('react-dom/server');
const rateLimit = require('express-rate-limit');
const app = express();
// 1. Request logging middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(
`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`
);
});
next();
});
// 2. Rate limiting
const apiLimiter = rateLimit({
windowMs: 60 * 1000,
max: 100,
message: { error: 'Too many requests' }
});
app.use('/api', apiLimiter);
// 3. Authentication middleware
async function requireAuth(req, res, next) {
const sessionToken = req.cookies['session'];
if (!sessionToken) {
if (req.path.startsWith('/admin') || req.path.startsWith('/dashboard')) {
return res.redirect('/login');
}
req.user = null;
return next();
}
try {
const session = await redis.get(`session:${sessionToken}`);
if (session) {
req.user = JSON.parse(session);
} else {
req.user = null;
}
} catch (error) {
req.user = null;
}
next();
}
app.use(requireAuth);
// 4. Caching middleware
function ssrCache(req, res, next) {
const key = `ssr:${req.originalUrl}`;
// Skip cache for authenticated users
if (req.user) return next();
redis.get(key, (err, cached) => {
if (cached) {
res.setHeader('X-Cache', 'HIT');
return res.send(cached);
}
res.setHeader('X-Cache', 'MISS');
// Capture render output for caching
const originalSend = res.send.bind(res);
res.send = (html) => {
redis.setex(key, 300, html); // Cache for 5 minutes
originalSend(html);
};
next();
});
}
app.get('/products/:id', ssrCache, async (req, res) => {
const product = await db.products.findById(req.params.id);
const html = renderToString(
React.createElement(ProductPage, { product, user: req.user })
);
res.send(html);
});
Common Mistakes
- Middleware that is too complex. Middleware should be fast and focused. Every millisecond in middleware adds to TTFB. Avoid heavy operations (database queries, external API calls) in middleware.
- Not excluding static files. Middleware should not Process static files (images, CSS, JS). Use config.matcher in Next.js or conditional checks in Express to skip them.
- Authentication in middleware without proper redirects. When redirecting to login, preserve the original URL so the user returns to their intended page after login.
- Memory leaks from middleware. Middleware that creates closures or event listeners can leak memory. Test under load to ensure no leaks.
- Ordering dependencies. Middleware runs in order. Security middleware should run before caching middleware. Logging should run after security but before rendering.
Practice Questions
- What is the purpose of middleware in SSR applications?
- How do you implement authentication middleware in Next.js?
- How do you set cache headers in SSR middleware?
- Why should middleware avoid heavy operations?
- How do you exclude static files from middleware processing?
Challenge: Build a complete middleware pipeline for a Next.js SSR application: authentication middleware that checks sessions and redirects to login, logging middleware that tracks request duration and status, caching middleware that sets appropriate Cache-Control headers, bot detection middleware that sets X-Robots-Tag, URL redirect middleware for legacy paths, and security headers middleware.
FAQ
Mini Project
Build a middleware pipeline for an SSR application: authentication middleware for admin routes (redirect to login), logging middleware that logs request method, path, status, and duration, caching middleware that sets Cache-Control headers based on URL patterns, bot detection middleware that adds X-Robots-Tag header for known bots, and URL redirect middleware that handles 10 legacy URL patterns.
What's Next
You understand SSR middleware. Now explore SSR Deployment to deploy SSR applications to production.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro