Cookies are the unsung heroes of web applications, silently carrying state between your browser and a server. k6, a modern load testing tool, needs to play by the same rules if it’s to simulate real user behavior accurately.
Let’s see k6 handle a simple login scenario, where a session cookie is set by the server and then used in subsequent requests.
import http from 'k6/http';
import { check } from 'k6';
export const options = {
vus: 1,
duration: '1m',
};
export default function () {
// Step 1: Log in and get the session cookie
const loginRes = http.post('https://test-api.k6.io/auth/token/login/', {
username: 'your_username', // Replace with actual username
password: 'your_password', // Replace with actual password
});
check(loginRes, {
'Login successful (status 200)': (r) => r.status === 200,
'Session cookie received': (r) => r.cookies.session !== undefined,
});
// Extract the session cookie value
const sessionCookie = loginRes.cookies.session[0]; // k6 returns cookies as an array
// Step 2: Make a request using the session cookie
const profileRes = http.get('https://test-api.k6.io/user/me/', {
headers: {
// Manually set the cookie header
'Cookie': `session=${sessionCookie}`,
},
});
check(profileRes, {
'Profile request successful (status 200)': (r) => r.status === 200,
'User ID in profile response': (r) => profileRes.json('id') !== null,
});
}
This script simulates a user logging in and then fetching their profile. Notice how we manually extract the session cookie from the loginRes and then construct the Cookie header for the profileRes. This is the fundamental dance of cookie management.
But manually managing cookies like this quickly becomes cumbersome. What if there are multiple cookies? What about their attributes like Path, Domain, Expires, Secure, HttpOnly? k6 has a more robust way.
The http.cookieJar() function provides a persistent cookie store that automatically manages cookies for you across requests within the same k6 VU (Virtual User).
import http from 'k6/http';
import { check } from 'k6';
export const options = {
vus: 1,
duration: '1m',
};
// Create a cookie jar for this VU
const jar = http.cookieJar();
export default function () {
// Set the cookie jar on the default http client
// This means all subsequent http requests will use this jar
http.setCookieJar(jar);
// Step 1: Log in. The cookie jar will automatically capture the 'session' cookie.
const loginRes = http.post('https://test-api.k6.io/auth/token/login/', {
username: 'your_username', // Replace with actual username
password: 'your_password', // Replace with actual password
});
check(loginRes, {
'Login successful (status 200)': (r) => r.status === 200,
'Session cookie captured by jar': () => jar.cookies('https://test-api.k6.io').session !== undefined,
});
// Step 2: Make a request using the session cookie.
// The cookie jar automatically sends the 'session' cookie.
const profileRes = http.get('https://test-api.k6.io/user/me/');
check(profileRes, {
'Profile request successful (status 200)': (r) => r.status === 200,
'User ID in profile response': (r) => profileRes.json('id') !== null,
});
}
By using http.cookieJar() and http.setCookieJar(), k6 takes on the role of a browser, automatically storing cookies received from the server and sending them back on subsequent requests to the appropriate domain and path. This simplifies your script significantly and ensures state is maintained correctly, just like a real user’s browser would.
The cookieJar object itself offers methods to inspect and manipulate cookies. You can get all cookies for a specific URL using jar.cookies('https://example.com') which returns an object where keys are cookie names and values are arrays of cookie values. You can also delete cookies with jar.delete('https://example.com', 'session') or jar.deleteAll().
One subtle but critical aspect is how k6 handles cookie attributes beyond just the name and value. When a server sends a Set-Cookie header like Set-Cookie: session=abc123xyz; Path=/; Domain=test-api.k6.io; Expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure; HttpOnly, the cookieJar understands and respects these attributes. It won’t send the session cookie on requests to paths other than /, it will only send it to test-api.k6.io, and it will respect the Expires date. The Secure and HttpOnly flags are also honored, meaning the cookie won’t be sent over non-HTTPS connections (if Secure is present) and won’t be accessible via JavaScript (if HttpOnly is present), mirroring browser behavior.
This deep understanding of cookie semantics is what allows k6 to produce load tests that are truly representative of real-world user interactions, especially for applications that rely heavily on session management.
The next hurdle is handling more complex authentication flows, like OAuth2, which often involve multiple redirects and token exchanges, each potentially setting or requiring cookies.