This is a long post.
Having investigated this issue for the last few days, I believe that there is a significant issue with the cookie implementation in BlackBerry browsers using the default Internet Browser. I haven’t been able to test the recently-released BlackBerry Bold (9000), which is bundled with the new BlackBerry Browser, but as far as I can determine, devices like the 8800 and 8820 are affected.
The problem occurs when a site attempts to set multiple cookies. Although they are stored on the device, the cookies are returned to the server haphazardly. Sometimes all cookies are returned; more often it’s only the first or last cookie that was set that is passed back.
The following is a TCP dump of traffic to a particular server. I’ve bolded the relevant parts and changed a few names. The server is not load-balanced, and the headers are not altered on my end in any way.
profile: http://www.blackberry.net/go/mobile/profiles/uaprof/8820/4.2.2.rdf
x-wap-profile: "http://www.blackberry.net/go/mobile/profiles/uaprof/8820/4.2.2.rdf"
Accept: application/vnd.rim.html,text/html,application/xhtml+xml,application/vnd.wap.xhtml+xml,text/vnd.sun.j2me.app-descriptor,image/vnd.rim.png,image/jpeg,application/x-vnd.rim.pme.b,image/gif;anim=1,application/vnd.rim.jscriptc;v=0-8-8,application/x-javascript,application/vnd.rim.css;v=1,text/css;media=handheld,application/vnd.wap.wmlc;q=0.9,application/vnd.wap.wmlscriptc;q=0.7,text/vnd.wap.wml;q=0.7,*/*;q=0.5
Accept-Charset: ISO-8859-1,UTF-8,US-ASCII,UTF-16BE,windows-1252,UTF-16LE,windows-1254,KOI8-R,windows-1251,windows-1255,windows-1256,windows-1250
Accept-Language: en-US,en;q=0.5
User-Agent: BlackBerry8820/4.2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/100
Host: cookie.example.com
Via: BISB_3.3.0.45, 1.1 pmds76.bisb1.blackberry:3128 (squid/2.5.STABLE12)
X-Forwarded-For: unknown
Cache-Control: max-age=259200
Connection: keep-alive
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 15:56:19 GMT
Set-Cookie: cookie1=o4s9o298mbqjf5qnjmgad76rs0; path=/; domain=cookie.example.com
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: cookie2=en_us; expires=Thu, 27-Aug-2009 15:56:19 GMT; path=/; domain=cookie.example.com
Content-Language: en_us
Set-Cookie: cookie3=2d14a216211bc7f1a1b03fa747d1087e; expires=Mon, 01-Sep-2008 06:59:59 GMT; path=/; domain=cookie.example.com
Vary: Accept-Encoding
Content-Length: 7975
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
Here we set three cookies: cookie1, cookie2, and cookie3.
User-Agent: BlackBerry8820/4.2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/100
profile: http://www.blackberry.net/go/mobile/profiles/uaprof/8820/4.2.2.rdf
Referer: http://cookie.example.com/
Host: cookie.example.com
Cookie: cookie1=o4s9o298mbqjf5qnjmgad76rs0
Via: 1.1 pmds76.bisb1.blackberry:3128 (squid/2.5.STABLE12)
X-Forwarded-For: unknown
Cache-Control: max-age=259200
Connection: keep-alive
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 15:56:21 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: cookie2=en_us; expires=Thu, 27-Aug-2009 15:56:21 GMT; path=/; domain=cookie.example.com
Content-Language: en_us
Set-Cookie: cookie3=a082e222cde7b4aedb6a8b42f2723849; expires=Mon, 01-Sep-2008 06:59:59 GMT; path=/; domain=cookie.example.com
Vary: Accept-Encoding
Content-Length: 3701
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/css
The BlackBerry (an 8820) returns only one cookie back (cookie1), so the server tries to resend the cookies it did not receive. Keep in mind the expiration times here were set in the future for this request.
User-Agent: BlackBerry8820/4.2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/100
profile: http://www.blackberry.net/go/mobile/profiles/uaprof/8820/4.2.2.rdf
Referer: http://cookie.example.com/
Host: cookie.example.com
Cookie: cookie1=o4s9o298mbqjf5qnjmgad76rs0
Via: 1.1 pmds76.bisb1.blackberry:3128 (squid/2.5.STABLE12)
X-Forwarded-For: unknown
Cache-Control: max-age=259200
Connection: keep-alive
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 15:56:21 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: cookie2=en_us; expires=Thu, 27-Aug-2009 15:56:22 GMT; path=/; domain=cookie.example.com
Content-Language: en_us
Set-Cookie: cookie3=270773eec992a03fd63a7b74b2f5ef9a; expires=Mon, 01-Sep-2008 06:59:59 GMT; path=/; domain=cookie.example.com
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=utf-8
This is the same as above. The next GET is really interesting:
User-Agent: BlackBerry8820/4.2.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/100
profile: http://www.blackberry.net/go/mobile/profiles/uaprof/8820/4.2.2.rdf
Referer: http://cookie.example.com/
Host: cookie.example.com
Cookie: cookie2=en_us
Cookie: expires=Thu, 27-Aug-2009 15:57:03 GMT
Via: 1.1 pmds76.bisb1.blackberry:3128 (squid/2.5.STABLE12)
X-Forwarded-For: unknown
Cache-Control: no-cache, max-age=259200
Connection: keep-alive
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 15:57:05 GMT
Set-Cookie: cookie1=32mrdio18lvpg6cduhnt1prmj5; path=/; domain=cookie.example.com
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: cookie2=en_us; expires=Thu, 27-Aug-2009 15:57:05 GMT; path=/; domain=cookie.example.com
Content-Language: en_us
Set-Cookie: cookie3=2d0b160a97e20469aad34e01890adbfb; expires=Mon, 01-Sep-2008 06:59:59 GMT; path=/; domain=cookie.example.com
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=utf-8
The BlackBerry sends back cookie2 and “expires” in two headers, as if they are two separate cookies!
For comparison, here’s a similar request from the same device accessing the same URL, but using Opera Mini instead. It performs as expected.
User-Agent: Opera/9.50 (J2ME/MIDP; Opera Mini/4.1.11355/546; U; en)
Host: cookie.example.com
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Connection: Keep-Alive
X-OperaMini-Features: advanced, file_system, folding
X-OperaMini-Phone-UA: BlackBerry
X-OperaMini-Phone: RIM #
x-forwarded-for: 206.53.144.82, unknown
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 17:14:47 GMT
Set-Cookie: cookie1=dpr07co4i5dchtgmk8g3gdt0b6; path=/; domain=cookie.example.com
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Set-Cookie: cookie2=en_us; expires=Thu, 27-Aug-2009 17:14:47 GMT; path=/; domain=cookie.example.com
Content-Language: en_us
Set-Cookie: cookie3=1ce651a62bfe62a25faff44e5e5d500b; expires=Mon, 01-Sep-2008 06:59:59 GMT; path=/; domain=cookie.example.com
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 2176
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
GET /image/example.gif HTTP/1.1
User-Agent: Opera/9.50 (J2ME/MIDP; Opera Mini/4.1.11355/546; U; en)
Host: cookie.example.com
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://cookie.example.com/
Cookie: cookie1=dpr07co4i5dchtgmk8g3gdt0b6; cookie3=1ce651a62bfe62a25faff44e5e5d500b; cookie2=en_us
Cookie2: $Version=1
Connection: Keep-Alive, TE
TE: deflate, gzip, chunked, identity, trailers
X-OperaMini-Features: advanced, file_system, folding
X-OperaMini-Phone-UA: BlackBerry
X-OperaMini-Phone: RIM #
x-forwarded-for: 206.53.144.82, unknown
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 17:14:49 GMT
Last-Modified: Wed, 20 Aug 2008 19:36:32 GMT
ETag: "2c"
Accept-Ranges: bytes
Content-Length: 44
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: image/gif
GET / HTTP/1.1
User-Agent: Opera/9.50 (J2ME/MIDP; Opera Mini/4.1.11355/546; U; en)
Host: cookie.example.com
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Cookie: cookie1=dpr07co4i5dchtgmk8g3gdt0b6; cookie3=1ce651a62bfe62a25faff44e5e5d500b; cookie2=en_us
Cookie2: $Version=1
Cache-Control: no-cache
Connection: Keep-Alive, TE
TE: deflate, gzip, chunked, identity, trailers
X-OperaMini-Features: advanced, file_system, folding
X-OperaMini-Phone-UA: BlackBerry
X-OperaMini-Phone: RIM #
x-forwarded-for: 206.53.144.186, unknown
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 17:16:08 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Language: en_us
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 2283
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
GET /subsequent/request HTTP/1.1
User-Agent: Opera/9.50 (J2ME/MIDP; Opera Mini/4.1.11355/546; U; en)
Host: cookie.example.com
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://cookie.example.com/
Cookie: cookie1=dpr07co4i5dchtgmk8g3gdt0b6; cookie3=1ce651a62bfe62a25faff44e5e5d500b; cookie2=en_us
Cookie2: $Version=1
Connection: Keep-Alive, TE
TE: deflate, gzip, chunked, identity, trailers
X-OperaMini-Features: advanced, file_system, folding
X-OperaMini-Phone-UA: BlackBerry
X-OperaMini-Phone: RIM #
x-forwarded-for: 206.53.144.186, unknown
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2008 17:16:09 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Language: en_us
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 958
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/css
GET /image/example.gif HTTP/1.1
User-Agent: Opera/9.50 (J2ME/MIDP; Opera Mini/4.1.11355/546; U; en)
Host: cookie.example.com
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://cookie.example.com/
If-Modified-Since: Wed, 20 Aug 2008 19:36:32 GMT
If-None-Match: "2c"
Cookie: cookie1=dpr07co4i5dchtgmk8g3gdt0b6; cookie3=1ce651a62bfe62a25faff44e5e5d500b; cookie2=en_us
Cookie2: $Version=1
Connection: Keep-Alive, TE
TE: deflate, gzip, chunked, identity, trailers
X-OperaMini-Features: advanced, file_system, folding
X-OperaMini-Phone-UA: BlackBerry
X-OperaMini-Phone: RIM #
x-forwarded-for: 206.53.144.186, unknown
HTTP/1.1 304 Not Modified
Date: Wed, 27 Aug 2008 17:16:10 GMT
Connection: Keep-Alive
Keep-Alive: timeout=5, max=98
ETag: "2c"
So what’s the solution in this situation? It looks like your best option is to set just one cookie—but shove all your values into it and delimit them in some way (and make sure it’s less than 1024 bytes). If you have multiple expire times, though, you will also have to handle expiration manually. Google does this:
Can everyone just buy an iPhone already?