Skip to main content

Express Cookie Handling

Cookies are small pieces of data that a server sends to a user's web browser. The browser may store these cookies and send them back to the server in subsequent requests. In web development, cookies serve various purposes, including session management, tracking user preferences, and implementing authentication systems.

In this guide, we'll explore how to handle cookies in Express.js applications effectively and securely.

Prerequisites

Before diving into cookie handling in Express, you should have:

  • Basic knowledge of Node.js and Express.js
  • A working Express.js application set up
  • Understanding of HTTP requests and responses

Express doesn't have built-in cookie handling, but it offers a middleware called cookie-parser that simplifies working with cookies. Let's start by installing it:

bash
npm install cookie-parser

Once installed, you need to set up the cookie-parser middleware in your Express application:

javascript
const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();

// Set up cookie parser middleware
app.use(cookieParser());

// Optional: You can provide a secret key for signed cookies
// app.use(cookieParser('your-secret-key'));

Setting Cookies

To set a cookie in an Express application, you use the res.cookie() method:

javascript
app.get('/set-cookie', (req, res) => {
// Set a simple cookie
res.cookie('username', 'john_doe');

res.send('Cookie has been set!');
});

Setting Cookies with Options

You can provide various options when setting cookies:

javascript
app.get('/set-cookie-with-options', (req, res) => {
// Set cookie with options
res.cookie('user_preferences', JSON.stringify({ theme: 'dark', fontSize: 'large' }), {
maxAge: 24 * 60 * 60 * 1000, // 24 hours in milliseconds
httpOnly: true, // Cookie is not accessible via JavaScript
secure: true, // Cookie only sent over HTTPS
sameSite: 'strict' // Helps prevent CSRF attacks
});

res.send('Cookie with options has been set!');
});

Here are some important cookie options you can use:

OptionDescription
maxAgeMaximum age of the cookie in milliseconds
expiresSpecific date when the cookie expires
httpOnlyIf true, cookie cannot be accessed by client-side JavaScript
secureIf true, cookie is only sent over HTTPS
domainDomain for which the cookie is valid
pathPath for which the cookie is valid
sameSiteControls when cookies are sent with cross-site requests (strict, lax, or none)

Reading Cookies

To read cookies that have been sent by the client, use the req.cookies object:

javascript
app.get('/get-cookies', (req, res) => {
const username = req.cookies.username;

if (username) {
res.send(`Hello, ${username}!`);
} else {
res.send('No username cookie found.');
}
});

Reading All Cookies

You can access all cookies sent by the client:

javascript
app.get('/all-cookies', (req, res) => {
res.json(req.cookies);
});

Signed Cookies

For additional security, you can use signed cookies, which are cookies that have been signed with a secret key to prevent tampering:

javascript
const app = express();
app.use(cookieParser('my-secret-key'));

// Setting a signed cookie
app.get('/set-signed-cookie', (req, res) => {
res.cookie('user_id', '12345', { signed: true });
res.send('Signed cookie set!');
});

// Reading a signed cookie
app.get('/get-signed-cookie', (req, res) => {
const userId = req.signedCookies.user_id;

if (userId) {
res.send(`User ID: ${userId}`);
} else {
res.send('No signed user ID cookie found.');
}
});

Clearing Cookies

To remove a cookie, use the res.clearCookie() method:

javascript
app.get('/clear-cookie', (req, res) => {
res.clearCookie('username');
res.send('Username cookie cleared!');
});

If the cookie was set with specific options like path or domain, you need to include the same options when clearing it:

javascript
app.get('/clear-specific-cookie', (req, res) => {
res.clearCookie('user_preferences', {
path: '/',
domain: 'example.com'
});

res.send('User preferences cookie cleared!');
});

Practical Example: User Theme Preference

Let's build a simple example that uses cookies to remember a user's theme preference:

javascript
const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser());
app.use(express.urlencoded({ extended: true }));

// Serve an HTML form for selecting theme
app.get('/', (req, res) => {
const currentTheme = req.cookies.theme || 'light';

res.send(`
<html>
<head>
<title>Theme Selector</title>
<style>
body {
background-color: ${currentTheme === 'dark' ? '#333' : '#fff'};
color: ${currentTheme === 'dark' ? '#fff' : '#333'};
font-family: Arial, sans-serif;
padding: 20px;
}
</style>
</head>
<body>
<h1>Select Your Theme</h1>
<form action="/set-theme" method="post">
<select name="theme">
<option value="light" ${currentTheme === 'light' ? 'selected' : ''}>Light</option>
<option value="dark" ${currentTheme === 'dark' ? 'selected' : ''}>Dark</option>
</select>
<button type="submit">Save Preference</button>
</form>
<p>Your current theme is: ${currentTheme}</p>
</body>
</html>
`);
});

// Set the theme cookie based on form submission
app.post('/set-theme', (req, res) => {
const { theme } = req.body;

// Set cookie that expires in 30 days
res.cookie('theme', theme, {
maxAge: 30 * 24 * 60 * 60 * 1000,
httpOnly: true
});

res.redirect('/');
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});

Practical Example: Simple Authentication System

Here's a basic example of using cookies for authentication:

javascript
const express = require('express');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser('auth-secret'));
app.use(express.urlencoded({ extended: true }));

// Fake user database
const users = {
'user1': 'password1',
'user2': 'password2'
};

// Middleware to check if user is logged in
function isAuthenticated(req, res, next) {
const username = req.signedCookies.username;

if (username && users[username]) {
next();
} else {
res.redirect('/login');
}
}

// Login page
app.get('/login', (req, res) => {
res.send(`
<h1>Login</h1>
<form method="post" action="/login">
<div>
<label>Username:</label>
<input type="text" name="username">
</div>
<div>
<label>Password:</label>
<input type="password" name="password">
</div>
<button type="submit">Login</button>
</form>
`);
});

// Process login
app.post('/login', (req, res) => {
const { username, password } = req.body;

if (users[username] && users[username] === password) {
// Set a signed cookie on successful login
res.cookie('username', username, {
signed: true,
httpOnly: true,
maxAge: 3600000 // 1 hour
});

res.redirect('/dashboard');
} else {
res.send('Invalid username or password. <a href="/login">Try again</a>');
}
});

// Dashboard (protected route)
app.get('/dashboard', isAuthenticated, (req, res) => {
const username = req.signedCookies.username;

res.send(`
<h1>Welcome to your dashboard, ${username}!</h1>
<p>This is a protected route that requires authentication.</p>
<a href="/logout">Logout</a>
`);
});

// Logout
app.get('/logout', (req, res) => {
res.clearCookie('username');
res.redirect('/login');
});

app.listen(3000, () => {
console.log('Server running on port 3000');
});
  1. Use the httpOnly flag to prevent client-side JavaScript from accessing the cookie, reducing XSS risks.

  2. Use the secure flag to ensure cookies are only sent over HTTPS connections.

  3. Implement sameSite attribute to control when cookies are sent with cross-site requests:

    • strict: Cookies are only sent in first-party context
    • lax: Cookies are sent when the user navigates to the site from an external link
    • none: Cookies are sent on both same-site and cross-site requests (requires secure: true)
  4. Set appropriate expiration with maxAge or expires to limit the cookie's lifespan.

  5. Use signed cookies for any sensitive information to prevent tampering.

  6. Keep cookie data minimal - store only what's necessary.

Be aware of these limitations when working with cookies:

  1. Size limit: Browsers typically limit cookies to 4KB per domain.

  2. Number limit: Most browsers limit the number of cookies per domain (usually around 50).

  3. Privacy concerns: Due to regulations like GDPR and CCPA, you may need to get user consent for non-essential cookies.

Summary

In this guide, we've learned how to:

  • Set up the cookie-parser middleware in Express
  • Set cookies with various options
  • Read cookies sent by clients
  • Use signed cookies for added security
  • Clear cookies when they're no longer needed
  • Implement practical examples like theme preferences and simple authentication
  • Follow best practices for cookie security

Cookies are a fundamental part of web applications, but they should be used responsibly with security and privacy in mind. For more complex state management needs, consider using sessions or other state management solutions.

Additional Resources

Exercises

  1. Modify the theme preference example to include more theme options.
  2. Build a shopping cart system that stores cart items in cookies.
  3. Create a "remember me" feature for the authentication example.
  4. Implement a cookie consent banner that complies with privacy regulations.
  5. Extend the authentication system to have different user roles with different access levels.

Happy coding!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)