Published on November 30, 2022.
One of the projects that I've been working on is a Web portal for a NetSuite client. The portal is built on the NetSuite platform as a Suitelet. It uses a customized and enhanced version of my SuiteFrame framework, and is being served up behind NGINX (using this technique that I shared back in January).
While developing the portal, I needed to add support for sessions. I'll write about how I'm managing sessions in a future post.
One of the things that I needed for session management was a way to work with cookies. In this post, I'll share some of the code that I wrote to do that.
When using a Suitelet, we have access to all of a request's headers, and can get to them via context.request.headers. Here's a sample of what the headers look like:
{ "X-Akamai-SR-Hop": "1", "true-client-ip": "83.74.96.100", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15", "x-forwarded-port": "443", "x-advpf-parse-category": "DEFAULT_ALLHTML", "Accept-Encoding": "gzip", "True-Client-IP": "83.74.96.100", "via": "1.1 v1-akamaitech.net(ghost) (AkamaiGHost), 1.1 akamai.net(ghost) (AkamaiGHost)", "x-forwarded-host": "9999999-sb3.extforms.netsuite.com:443, 9999999-sb3.extforms.netsuite.com", "host": "9999999-sb3.extforms.netsuite.com", "X-Advpf-Parse-Category": "DEFAULT_ALLHTML", "ns-client-ip": "83.74.96.100", "connection": "keep-alive", "cache-control": "no-cache, max-age=0", "X-Real-IP": "55.140.23.28", "Cookie": "sessionID=20170094-2961ae54-de92-49ac-a0a8-93f13786fede; NS_ROUTING_VERSION=LAGGING; c7tXy98SOdcsRmaJORP2xg=AAABhHfmy1MPCnHzorzdLrEhYIuHIuNVNakHqWKIjIhEl5W8hlAI5A", "NS-Client-IP": "83.74.96.100", "Akamai-Origin-Hop": "2", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "cookie": "sessionID=20170094-2961ae54-de92-49ac-a0a8-93f13786fede; NS_ROUTING_VERSION=LAGGING; c7tXy98SOdcsRmaJORP2xg=AAABhHfmy1MPCnHzorzdLrEhYIuHIuNVNakHqWKIjIhEl5W8hlAI5A", "x-forwarded-proto": "https", "accept-language": "en-US,en;q=0.9", "X-Forwarded-Proto": "https", "X-Forwarded-Host": "9999999-sb3.extforms.netsuite.com:443, 9999999-sb3.extforms.netsuite.com", "Akamai-GRN": "0.4f6adc17.1669841763.d8873b13, 0.378cd017.1669841763.4e34b9ba", "Connection": "keep-alive", "Host": "9999999-sb3.extforms.netsuite.com", "Pragma": "no-cache", "x-forwarded-for": "83.74.96.100, 23.220.106.79, 55.140.23.28, 10.136.0.13", "X-Forwarded-Port": "443", "akamai-origin-hop": "2", "pragma": "no-cache", "Via": "1.1 v1-akamaitech.net(ghost) (AkamaiGHost), 1.1 akamai.net(ghost) (AkamaiGHost)", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "x-real-ip": "55.140.23.28", "x-akamai-sr-hop": "1", "X-Akamai-CONFIG-LOG-DETAIL": "true", "x-akamai-config-log-detail": "true", "x-forwarded-server": "forms.na7.netsuite.com", "Cache-Control": "no-cache, max-age=0", "X-Forwarded-For": "83.74.96.100, 23.220.106.79, 55.140.23.28, 10.136.0.13", "Accept-Language": "en-US,en;q=0.9", "X-Forwarded-Server": "forms.na7.netsuite.com", "accept-encoding": "gzip", "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.2.35 (KHTML, like Gecko) Version/15.16.1 Safari/605.2.35", "akamai-grn": "0.4f6adc17.1669841763.d8873b13, 0.327cd017.1669841763.4e34b9ba" }
Each header is listed twice, once with header name in lower case, and a second time in intercaps. I usually refer to the lower case versions, but I don't think it matters all that much.
Cookies are available via the "cookie" header. It looks like this:
"cookie": "sessionID=20170094-2961ae54-de92-49ac-a0a8-93f13786fede; NS_ROUTING_VERSION=LAGGING; c7tXy98SOdcsRmaJORP2xg=AAABhHfmy1MPCnHzorzdLrEhYIuHIuNVNakHqWKIjIhEl5W8hlAI5A"
So we can reference it using context.request.headers['cookie']. That's easy enough. But that returns the entire cookie string value. To parse it, I wrote this function, which takes the script context, gets the cookie header, parses it, and returns it as an object.
function cookiesGet( context ) { var cookies = {} var cookiesOriginal = context.request.headers['cookie']; if ( cookiesOriginal == null ) { return cookies; } cookiesOriginal = cookiesOriginal.split("; "); for ( var i = 0; i < cookiesOriginal.length; i++ ){ var thisCookie = cookiesOriginal[i]; thisCookie = thisCookie.split("="); cookies[thisCookie[0]] = thisCookie[1]; } return cookies; }
The result will look something like this:
{ "sessionID": "20170094-2961ae54-de92-49ac-a0a8-93f13786fede", "NS_ROUTING_VERSION": "LAGGING", "c7tXy98SOdcsRmaJORP2xg": "AAABhHfmy1MPCnHzorzdLrEhYIuHIuNVNakHqWKIjIhEl5W8hlAI5A" }
To set cookies in a Suitelet, we can use the context.response.setHeader method. For example, to drop a cookie with name-value pair "a=1" you would use something like this:
context.response.setHeader( { name: 'Set-Cookie', value: 'a=1; path=/' } );
Dropping multiple name-value pairs can be tricky. For example, to set a second name-value pair ("b=2") you might try this...
context.response.setHeader( { name: 'Set-Cookie', value: 'a=1; b=2; path=/' } );
But the resulting cookie will only include the first name-value pair, and ignore the second.
Calling the setHeader method twice, like this...
context.response.setHeader( { name: 'Set-Cookie', value: 'a=1; path=/' } ); context.response.setHeader( { name: 'Set-Cookie', value: 'b=2; path=/' } );
... will result in a cookie that includes only the second name-value pair.
One method that I've found to solve this issue is to put the values in an object, JSON-encode the object, and then URL-encode the JSON string. For example...
let values = {a: 1, b: 2} values = JSON.stringify(values); values = encodeURIComponent(values); context.response.setHeader( { name: 'Set-Cookie', value: `values=${values}; path=/` } );
You would then read the cookie, and decode it. For example...
let cookies = cookiesGet( context ); let values = cookies.values; values = decodeURIComponent(values); values = JSON.parse( values );
I hope you find this short post to be helpful.
Happy SuiteScripting!
Hello, I’m Tim Dietrich. I design and build custom software for businesses running on NetSuite — from mobile apps and Web portals to Web APIs and integrations.
I’ve created several widely used open-source solutions for the NetSuite community, including the SuiteQL Query Tool and SuiteAPI, which help developers and businesses get more out of their systems.
I’m also the founder of SuiteStep, a NetSuite development studio focused on pushing the boundaries of what’s possible on the platform. Through SuiteStep, I deliver custom software and AI-driven solutions that make NetSuite more powerful, accessible, and future-ready.
Copyright © 2025 Tim Dietrich.