Sessions are tracked using a unique session id generated for each client by the container; they're needed since HTTP is a stateless protocol.
Initially, when the client connects to the server, it doesn't have a session id assigned to it. The server then sends a response with a session id in the response headers. But the server doesn't know if the browser supports sessions using cookies so it also uses URL rewriting ( but if you use the appropriate methods in your code ).
When the client sends the next request, the server will know whether the browser supports cookies or not: if there is a header, the browser supports it and the server will not use the rewritten URLs in it's communication with this client, only cookies will be used so the encodeURL()/ encodeRedirectURL() won't be used; if there isn't then the server must use URL rewriting otherwise the session will not be tracked, so now the server will not send the session in the header as a cookie but will use URL rewriting.
This is how a smart container would work, a dumb one would continue to use both methods always without checking if the one was needed or even working.
J2EE specifies that the session cookie is named JSESSIONID and if you looked at the request/ response you'd see something like:
POST /select/selectBeerTaste.do HTTP/1.1 User-Agent: Mozilla/5.0 Cookie: JSESSIONID=0AAB6C8DE415
If cookies aren't enabled and you use URL rewriting, you'll see URL's like:
The way the session id is appended to the URL depends on the vendor, the example above is how Tomcat does it.
So now, if the client supports cookies, the container can get the session id from the request header. If not, and if you've been smart enough to use HttpServletResponse.encodeURL() and HttpServletResponse.encodeRedirectURL(), it'll append the session id to the URLs in some vendor-specific way and still manage to track the session.