Testing API Headers in Playwright

Published on March 28, 2025

👋Hello technical folks ~

Most, if not all, of us should be familiar with Application Programming Interfaces (APIs).

API is a set of rules and protocols which allows different software applications to interact and communicate with each other, enabling them to share data, features, and functionalities.

Each API (request and response) contains a set of key-value pairs of metadata, known as HTTP headers, providing critical details about the transferred data. In this article, we’ll be looking at:

  • how to grab HTTP headers from each API response in Playwright, and
  • what to check for these headers

Getting API (response) headers

To retrieve the set of API headers associated with the API, we’ll need to set up the APIRequestContext object to call the endpoint(s) and get the headers from its response.

// file: security-headers.test.ts

import { APIRequestContext, APIResponse } from '@playwright/test';

let headersArr: Array<{name: string; value: string}>; // for S3a
let headersObj: {}; // for S3b

test.beforeAll(async ({ }) => {
// S1: Set up APIRequestContext
// ...

// S2: Send an API request
const requestPayload = { "key1": "val1", "key2": "val2" };
const response = await apiContext.post(endpointURL, { data: requestPayload });
// for GET request, it's:
// const response = await apiContext.get(endpointURL);

// S3a: Get HTTP headers (either this)
headersArr = response.headersArray();
// output:
// [ {name: 'Content-Type', value: 'application/json; charset=utf-8'},
// {name: 'Content-Length', value: '66' },
// ...
// ]

// S3b: Get HTTP headers (or this)
headersObj = response.headers();
// output:
// { 'content-type': 'application/json; charset=utf-8',
// 'content-length': '66',
// ...
// }
});

For this article, we will be using headersArray() to retrieve an array of objects.

What to check for API headers

Here’s a non-exhaustive list of tests to include:

// file: security-headers.test.ts
import { APIRequestContext, APIResponse } from '@playwright/test';

let headersArr: Array<{name: string; value: string}>;

test.beforeAll(async ({ }) => {
// refer to S1, S2, and S3a above
});

test(`'Content-Type' must contain 'application/json'`, async () => {
const resultObj = headersArr.find(({ name }) => name === "Content-Type");

expect(resultObj?.value).toContain("application/json");
});

test(`'x-frame-options' is set to 'SAMEORIGIN'`, async () => {
const resultObj = headersArr.find(({ name }) => name === "x-frame-options");

expect(resultObj?.value).toContain("SAMEORIGIN");
});

test(`'cache-control' must contain 'no-store'`, async () => {
const resultObj = headersArr.find(({ name }) => name === "cache-control");

expect(resultObj?.value).toContain("no-cache");
});

test(`'referrer-policy' must be set to 'no-referrer'`, async () => {
// add code here
});

test(`'server' must be set to 'undefined' or be removed`, async () => {
// add code here
});

test(`'x-content-type-options' must be set to 'nosniff'`, async () => {
// add code here
});

test(`'referrer-policy' must be set to 'no-referrer'`, async () => {
// add code here
});

// more tests here...

API headers provide format of the data being sent/received, authorisation credentials (e.g. API keys or tokens), and other metadata (e.g. response cookies, caching) about the request and response. Therefore, having properly configured HTTP response headers is very important; it can help prevent security vulnerabilities such as Cross-Site Scripting, ClickJacking, and Information Disclosure.

We hope this article will help enhance on your current test suite! Also, take a look at the other Playwright (JS) articles we’ve written:

🧙🏼‍♀Team Merlin 💛
Application security is not any individual’s problem but a shared responsibility.


Testing API Headers in Playwright was originally published in Government Digital Products, Singapore on Medium, where people are continuing the conversation by highlighting and responding to this story.