Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2018 (Hollywood, CA) with: Azeez Olaniran and Adebayo Maborukoje and Jorg Are
Ben Nadel at InVision In Real Life (IRL) 2018 (Hollywood, CA) with: Azeez Olaniran Adebayo Maborukoje Jorg Are

Ask Ben: Ending ColdFusion Session When User Closes Browser

By
Published in , Comments (60)

This wasn't quite an Ask Ben question, but it was a question that was asked of me and I would like to have a definitive place to point people when the question comes up again. A user asked me how to get ColdFusion to end the user's session when the user closes their browser. I have discussed ending sessions before, but let's just quickly hammer out a few points: you cannot force a session to end because a session is not something that is running (in the way that you think of a Windows application as running). These are all just chunks of memory space that get associated with different users. The best we can do is prevent a users' browser from re-associating with a given session on page refresh or page load. This will get the user to create a NEW session, but won't technically end the old session.

That being said, in order to get the user to create a new session when they re-open their browser (which can be thought of as ending their old session when the browser closes), we have to create session-only cookies. A session-only cookie is a cookie that is stored in the browser's memory but is never actually written to the user's hard drive. Therefore, when the browser closes and the memory space is flushed, the session-cookies disappear with it.

Creating a session-only cookie is rather easy. All we have to do is use ColdFusion's CFCookie tag to set the cookie values and exclude the "expires" attribute. The only problem we run into is that ColdFusion automatically sets the user's cookies when the session first runs. To prevent this from happening, we have to turn off client cookies as part of the application setting. Here is an Application.cfc component that demonstrates these two steps:

<cfcomponent
	output="false">

	<!--- Define the application settings. --->
	<cfset THIS.Name = "SessionOnlyCookiesTest" />
	<cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 5, 0 ) />
	<cfset THIS.SessionManagement = true />
	<cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 1, 0 ) />

	<!---
		When creating session-only cookies so that the user's
		session ends when the browser is closed, we must turn
		off ColdFusion's automatic cookie storage. If we let
		ColdFusion store the cookies, then it will set an
		expiration date which will cause us trouble.
	--->
	<cfset THIS.SetClientCookies = false />


	<cffunction
		name="OnSessionStart"
		access="public"
		returntype="void"
		output="false"
		hint="Fires when user session initializes (first fun).">

		<!---
			Since we prevented ColdFusion from writing the
			client cookied, we have to take it upon ourselves
			to write the cookies so that the user session will
			hold from page to page. We can use CFCookie to
			create these session-only cookies. By using the
			CFCookie tag without using the Expires attribute,
			it will get the browser to convert the the stored
			cookie values into session-only values.
		--->
		<cfcookie name="CFID" value="#SESSION.CFID#" />
		<cfcookie name="CFTOKEN" value="#SESSION.CFTOKEN#" />

		<!--- Store date the session was created. --->
		<cfset SESSION.DateInitialized = Now() />

		<!--- Return out. --->
		<cfreturn />
	</cffunction>

</cfcomponent>

Notice that in the application settings, we are turning off the client cookies; but then, as soon as ColdFusion triggers the OnSessionStart() application event, we are using the SESSION's CFID and CFTOKEN values to manually create the session-only cookies. This will allow the browser to "hand shake" with the user's existing session because while the browser is running, it uses session-only cookies and hard-drive-persisted cookies in the same manner. That's all there is to it. This will ensure that when the user closes and re-opens their browser, they will get a new ColdFusion session.

If you want to test to make sure this works, you can use this simple page:

<cfoutput>

	Session started:
	#TimeFormat( SESSION.DateInitialized, "hh:mm:ss" )#
	<br />

	Page Run:
	#TimeFormat( Now(), "hh:mm:ss" )#

</cfoutput>

Here, I am just outputting the current date and the date the session was initialized. If the session was just created (new session), then both those time stamps should be the same.

Want to use code from this post? Check out the license.

Reader Comments

15,902 Comments

@Scott,

I have never used J2EE session variables. Does that force a session-only cookie by default? Or is something more complicated going on behind the scenes?

21 Comments

@Ben - I am not really sure of the mechanism. However, I should note that the keys in the session scope are different when J2EE session variables are enabled. If you have apps that look for stuff like CFID, sessionID, etc, they could break if you enable J2EE session variables.

15,902 Comments

@Scott,

Sounds good. I remember having to learn something about it when I was studying for certification, but as I never use it, it has slipped away. I vaguely remember it having a dashed format that made me think of UUIDs, but not quite that big.

54 Comments

Yeah I can agree that J2EE is the way to go, I was poiinted in this direction a fair while back and made the switch over. I think the origional conversation I was having was with regards to session hijacking and that how using J2EE will effectivly help secure down the session against hijacking more effectivly as once the browser has been closed the session is unobtainable again, quite whether this is true or not I dont know.

One thing worth baring in mind, this only works when the entire browser is closed, you'll generaly find that with tabbed browing, closing the single tab alone is not enough to 'kill' the session.

Cheers Ben,

Rob

21 Comments

@Robert - I believe we had that conversation in IRC.

It is also worth noting that when you close the browser, the session is not killed. The session will still last as long as the timeout will allow. Rather, when the browser is opened again, a NEW session is started.

54 Comments

Thanks for clarifying that Scott,

I thought that would be the case, I was a little unsure as to the actual case so put my kill in inverted commas.

Cheers Mate,

Rob

15,902 Comments

@Robert,

Good call on the tabbed browsing. I know that I use tabbed browsing all the time. I assume this tabbed browsing issue affects both the standard cookie usage as well as the JSessionID stuff.

@Daniel,

Thanks for pointing out Gary's blog post. For some reason, I didn't see that before. Good stuff and good explanation of the benefits of JSessionsID.

18 Comments

Given that some users close their browser RATHER than "logging off", I don't suppose there's a way for me to determine when they've closed their browser and killed their session cookie. If I wanted to run a cleanup function onSessionEnd (to store final user state or update stats), it will still wait for the session timeout to occur. Does that sound right?

54 Comments

@ Dan - You're quite right, onSessionEnd() will still run, but not untill the users session times out, as Scott talks about above, the session isnt actualy killed when the browser is closed, just a new one started when the browser is reopened.

Hope that helps mate,

Rob

6 Comments

I have actually been tasked at work recently with finding a way to actually destroy session data when the user closes their browser. As several people have mentioned here, the session is not actually destroyed when the user closes their browser.

The best idea I have come up with so far for handling the browser closing instance, is to use the JavaScript onunload event handler and Ajax to tell the server to destroy the user's session (ie. call some method or template that would do <code>structDelete(session)</code> ). The only downside is that this event is triggered either when the user closes their browser or navigates away from the page. I am still trying to determine if it's possible to differentiate between a window close or trying to go to a different page. I think this event would also be triggered when closing a tab, but since it is JavaScript, it can certainly be browser specific, and I haven't really had a chance to try and implement this yet.

15,902 Comments

@Justin,

I get very cautious about this kind of activity. It can severly limit the way people experience a site. What if someone navigates a sight in several tabs? Or several windows (holding down CTRL or SHIFT when clicking links respectively)? When you start to become overly worried about ending sessions, you start to curb the ease of site usage.

I think what you need to do is ask yourself / your boss WHY the session needs to be ended? Don't just try to implement solutions - question whether or not they are useful.

54 Comments

Yeah,

I would agree with Ben on this I think, I'd be interested to see a decent Use Case for when destroying the sessions explicitly would be needed. I should think that using J2EE sessions which are cleanly assigned to each brower instance makes much more sense.

Even if you did find a hack which used JavaScript to detect the browser close / tab close, you're still reliant on the user having JavaScript enabled in thier browser in order for it to work, which at _best_ is just going to create inconsistant results.

I'd really not be too worried about chasing down a solution for this Justin. Just get yourself running on J2EE and leave it to work its magic and accept that sessions will live for a few minutes after the browser / tab is closed.

Rob

6 Comments

I agree that JavaScript is not necessarily the way to go. At best, it is only going to work some of the time. I even personally suggested just reducing the session timeout, which I think will solve the issue in my particular case.

I have been using J2EE sessions for quite a long time. The problem with sessions in general is that if you don't explicitly clear them (i.e. when the user logs out), then they are going to sit there taking up space (usually memory) until they time out. Setting a short timeout is a good start. For most apps, this will likely be all you need. However, I could certainly see a situation in a high volume application, where you may not want that session data to even sit around unused at all after the user closes their browser. There is no perfect solution to this problem, but I feel if this problem did need to be solved, the JavaScript onunload + Ajax method (that I poorly described in my previous post) might be the best way to go.

6 Comments

@Ben,

I am not simply trying to implement something that my boss has asked me to do in this case. On the contrary, I have personally suggested not using any client side code to instantly clear the session, unless we find it to be absolutely necessary.

Let me give you some perspective on this situation.

For one, the application is a legacy app, that is not particularly well written and is an absolute memory hog. We're currently trying to get time and funding to rebuild it, but for now, we have to deal with what we've got.

We basically need all the memory we can get. We ran some simulated load tests, with a normal loads for us of 20,000 - 30,000 users, and we noticed one of the issues was with how much memory unused sessions were taking up in some cases. The first step is to reduce the session timeout. Once the timeout is reduced (likely will reduce it to a 15 or 30 minute timeout) , if we still don't see the numbers we want to see, then we have to take additional measures to clear out the session data as soon as the user is "done" using the site.

Yes, the JavaScript way is not the cleanest solution. It is also not straighforward, because, as you mentioned, you want to have a minimal impact on the user experience. I am hoping it will not even be necessary to implement this at all, but we have not had the chance to conduct follow up tests yet. If I end up being in the situation where it does become necessary to design and implement something to clear the session at the moment the user is "done" with the site, I feel the onunload + Ajax approach in JavaScript will be the best way to go. It will only work some of the time at best, since it depends on the browser settings (though most of that is fixed in our case).

Thanks for the feedback.

15,902 Comments

@Justin,

If memory is a huge issue, here's what you should do:

1. Set the session timeout to like 2 minutes.

2. Create a "ping" on the page that calls the server to make sure the session doesn't die while the session is still open. Something extremely simple like:

setInterval(
. . . . function(){
. . . . . . . . var img = new Image();
. . . . . . . . img.src = "ping.cfm";
. . . . },
. . . . (1000 * 60)
. . . . );

This will call "ping.cfm" every 60 seconds and keep the session open.

This way, you don't have long session - you put the burden on the browser to keep the session alive by always pinging the server.

Then, make ping.cfm do almost nothing (no processing). Somethig like:

<cfheader name="content-length" value="0" />
<cfcontent type="image/gif" variable="#ArrayNew( 1 )#" />

Just a suggestion.

6 Comments

@Ben

That's a good idea. The only problem is while that handles the situation better when the user closes the browser, we still want the session to timeout if the user should get up and walk away for a while, and I believe the solution you proposed would keep the session alive forever as long as the browser is open. Basically, the idea here is to keep the session data for the shortest amount of time necessary, in all situations. In the case where a user does not close their browser and is inactive, this would be for the duration of the timeout period. Otherwise, it would be when the user closes their browser (or potentially, even navigates away from the landing page, though I'm not big on that idea at all).

Like I said, I sincerely hope to be able to avoid having to mess with this, as it is a very non-trivial problem, and feels like a bit of a hack job. However, I do want to have a good plan just in case a shorter session timeout isn't good enough. However, I'm pretty confident that reducing the timeout, in my situation, will be all that's needed.

Appreciate the ideas.

15,902 Comments

@Justin,

If you want the session to end when the user walks away from the browser for a while (leaving it open), why not just make the session really small?

Put it to like 5 minutes. What are the worst case scenarios of that?

54 Comments

@ Justin

My thoughts on ways in which you can appraoch the problem would be like this:

1. As you've discussed, look at shortening the length of the session timeout, this will without doubt help, its just a case of how much it will help.

This will only really work if you have a high turnover of short sessions, for instance, people come to your site to quickly buy something, or check an email or two, if its lots of long running sessions, such as a CRM app that clients log into in the morning and stay on all day, you wont really see much gain from this approach as the timeout 'overlap' period for sessions will be less frequent.

2. Upgrade the memory in your system. If you're not already running at full memory capacity, this will be by far the most cost effective way to ensure performance on the server and give you that little extra space.

3. Look at the app redesign, if your session size if the issue, look at moving away from a session based storage mechanism for complex objects or large amount of data, simple store a user id in the session and then call the required data on each page load, this will put more load on the server and likely the database as you're losing the caching benefits of the session scope, however, it'll save you heaps of memory.

4. Ben's solution does make sense.

Just my thoughts,

Rob

6 Comments

@Ben

5 minutes might be a little short, but regardless, reducing the timeout is the simplest solution which I am hoping will solve the majority of the session related issues until the time when we get funding to basically start over from the beginning with the design

@Rob

Our users may have the app open all day, but they are likely not actively using it all day. The most common situation will be that a user is idle for an extended period of time. Even in such a case, if the session does time out, they don't even have to log in again, because we use a company wide single sign on system, and as long as they are authenticated through that (which is valid for a 12 hour time span), our system will automatically create a new session if necessary. We have plenty of memory, and to be honest, I think we have increased the max amount of memory for the JVM too high (currently it reserves 3 GB for the application, which I think is too high). I work for a massive company, so typically, getting things like memory and disk space are not concerns. However, that's no excuse to have an app that poorly utilizes either :)

Thanks to everyone for all the feedback. It's much appreciated.

8 Comments

@Ben

So just to make things clear, there is no way to end the session when the browser closes and make the onSessionEnd() take place?

15,902 Comments

@wrighterb,

That is correct. The browser closing and the OnSessionEnd() are two separate events triggered by completely different systems.

50 Comments

is their a way to reset SessionTimeout on a keystroke ? like an onclick-something. So if someone is filling in a form it would just reset SessionTimeout every field or keystroke. So you could set shorter time period.

15,902 Comments

@James,

You can set up what's known as a "heart beat". You can have the client (browser) ping the server either periodically (ie. every X milliseconds) or have it ping the server given specific tasks (with some sort of buffer - you don't want to ping the server every key event).

14 Comments

Odd.

I've tried both Ben's example, as well as the J2EE variables setting, and in both cases, firefox persists my session after the browser closes! IE 6 and 7 does not (it works properly), but FF (version 3.0.11) will reattach to the previous session in ben's example, after a browser close. I also verified that the FF process had truly ended in the windows process manager.

Has anyone experienced something similar; not being able to get FF to release the session even if the app was coded to use in-browser cookies?

-Brian

50 Comments

Wow nice new site design - like it !_!
delete this if you want i dont intend to spam.
I really like the alternating photos of you and friends. Just brings more life to blog. Have a good day.

15,902 Comments

@Thiago,

I am not sure if that is possible. I can't see how the server would be able to differentiate between new page requests and new tabs. I would suggest not worrying about that - tabbing is a natural part of the web experience.

3 Comments

Hi again Ben!

I was wondering if something like the CFID and CFtoken would tell me when the user opens a new tab or a new window.

Actually, I don't want to know whether is a tab or a window, I just need to know if it's a new one.

It's a requirement that if the user opens a new window/tab the app asks him to login again.

Yes, I know! What **** requirement but there's no way out of it!

By the way, thanks for the answer and for the help!

15,902 Comments

@Thiago,

I do not think it is possible, at least at the server level. In Javascript, perhaps you can check to see if there is a "history". If there is no item in the history collection, then the window / tab should be new. Of course, this depends on Javascript, which is not really a security practice.

3 Comments

Ben,

Thanks a lot for your help!

After a long discusion we convinced the client to let the users use more than one window/tab at a time without a new login!

Your phrase was used many time! "It's part of the web"!

Again, thanks a lot!

6 Comments

If you really wanted to force the user to login each time they opened a new tab couldn't you do something like the following:

1. Disable client cookies for your application (i.e. set this.SetClientCookies = false in Application.cfc)

2. At every URL except your login URL, expect that the cftoken and cfid (or jSessionId) are passed via URL or form parameters. For added security (i.e. protection from session hijacking), I would think you could encrypt the fields, though I guess it all depends on if you could decrypt them prior to ColdFusion needing those values to lookup the session info.

3. When users come to the "front" page of your site in a new window/tab (i.e. by entering the URL in the address bar, clicking a bookmark, etc.), the session related values (cftoken, cfid, jsessionid) will not be available in the URL or form scopes, so it should create a new session.

I have been out of CF development for a few months, and it has been a really really long time since I used sessions without storing client cookies, so I could be wrong here, but it seems like this could work.

Happy hacking,

Justin

15,902 Comments

@Thiago,

Glad to help out, if only with a few words.

@Justin,

That will work if you assume that the new Tab / Window is opened without doing something like SHIFT+Click. If you were to open an existing link in a new window, there really would be no way from the server standpoint to know this.

25 Comments

I have never used J2EE session variables. Does that force a session-only cookie by default? Or is something more complicated going on behind the scenes?

Regular ColdFusion sessions are identified by persistent cookies, CFID and CFTOKEN; while J2EE sessions are identified by a cookie, JSESSIONID, that exists in memory (so it expires when the browser is closed). When the cookies expire, the session doesn't really end, but it is orphaned (inaccessible to the user), and will end when it times out normally.

If you want to orphan a session because of some action by the user (such as a logout), then you can do this:

<!--- Orphan CF Session --->
<cfcookie name="CFID" expires="Now" />
<cfcookie name="CFTOKEN" expires="Now" />

<!--- Orphan J2EE Session --->
<cfcookie name="JSESSIONID" expires="Now" />

Hope this very late comment helps.

25 Comments

Neither on a new tab or a new window, because the cookies (both the CFID and CFTOKEN persistent ones and the JSESSIONID memory-resident one) will still be accessible to the browser.

25 Comments

Yes, if you expire the cookies (as shown a couple of posts above) a new session will be created. Don't delete the keys from the session scope, however:

<!--- What NOT to do --->
<cfset structDelete(session, "CFID") />
<cfset structDelete(session, "CFTOKEN") />

15,902 Comments

@Naveen,

The one thing I would caution about is preventing a user from using a *natural* style of navigation. Meaning, with today's browsers, tabbed navigation is not only available, but heavily used. As such, make sure you're not disabling that feature just for the sake of it.

5 Comments

Just an extension of the ping script would be to add a counter in the parameters so that you can trigger an event on the Nth call to the function. So if it is called every 30 seconds, and counter hits 10, 5 minutes have passed and you can forward the user (through javascript) to a "session expired" page which also flushes the server side session data from memory. I know this is a very late comment, but just a thought.

Am I off base on this?

5 Comments

@Naveen,

My comment was directed to Justin Holzer and his question about detecting a browser close and destroying the session. Ben Nadel proposed a ping script to use after setting the session timeout low to keep the session alive for those viewing the site. The side effect being a session that the session never times out if walked away from due to the timed pings. My addition was to count the "pings" and forward the browser to a cleanup script if X pings have occurred since the page loaded. No ajax needed.

16 Comments

Ben,
Your insights here and elsewhere help me much. On this subject, my CF8 system uses J2EE variables and I just added a logout page that I direct a logout link to. On that page is a typical logout message and only 2 other lines of code:

<cfset session.setMaxInactiveInterval(javaCast( "int", 1 )) />
<cfcookie name="jsessionid" value="" expires="now" />

This nicely kills my session and thus clears the memory in 1 sec. I tested this by emailing myself in the onSessionEnd method of my Application.cfc. I also had my server Admin ensure the session and memory was cleared at the same time.

I actually go one step further and setup 2 optional URL params for the logout page (protocol and target) that redirect to another domain after 'logging' the user out. This enables me to send users to other domains, but ensures that their sessions are ended and memory freed up before they move on.

Thanks again for your help.

16 Comments

I also just made a change to handle the case where a user selects the 'File/Exit' or 'X (Close)' in IE. I have a onUnload="logout()" attribute for the body tag (actually onUnload="@@(onunload)@@logout()" to handle other requests I might need to add later; also onbeforeunload works in Win XP IE 7 that my users only use). Then on my page template I added:

<cfajaxproxy cfc="#application.CompDotPath#.logout" jsclassname="logoutProxy" />

<script type="text/javascript">
// This is actually a seperate JS file being called.
logoutProxy = new logoutProxy();
function logout() {
// I conditionally do other stuff here not shown depending on the page.
// kill the session
logoutProxy.doLogout();
}
</script>

I created a CFC called logout.cfc that I call from the JS above (and will also invoke from my logout page mentioned in my last post).

<cfcomponent displayname="logout"
hint="Logs a user out who tried to exit their browser."
output="no">
<cffunction NAME="doLogout" ACCESS="remote" RETURNTYPE="void">
<cfset session.setMaxInactiveInterval(javaCast( "int", 1 )) />
<cfcookie name="jsessionid" value="" expires="now" />
</cffunction>
</cfcomponent>

This worked great. I need to also test trying to kill the browser process, although I'm happy to get my kill % up this high without user involvement for times the app is not being used (I do have a session timeout set for 30 min).

Any thoughts, concerns or suggestions for further improvement? Is there a condition that would not invoke the onUnload event that I need to consider?

15,902 Comments

@Brian,

I'm really glad that you're finding some stuff here useful. I like the direction you're moving in. One thing you might want to try - which was suggested to me - was that rather than using CFAjaxProxy, use something like jQuery where you can explicitly set the AJAX to execute in Synchronous timeline.

I believe that CFAjaxProxy can only interact in ASYNCHronous timing (which might not execute before the browser shuts down). If you force the single-threading, however, I think the browser will wait till the unload fires before it actually ends the browser process.... of course, this is just a theory.

16 Comments

Ben,
Thanks for the tip. I have not tried to incorporate jQuery in yet, but it is on my todo list to try. So far though my solution seems to be working correctly. I still need to roll it to our whole development site and confirm that the AJAX and onUnload event function does not break anything.

15,902 Comments

@Brian,

Hey, if it's working, then there's nothing to fix :) I have heard a few people say that the async requests do work just fine. I was just passing on some untested advice that I was given.

2 Comments

We've used code that sets the cookies without the "expires" attribute in most of our applications to accommodate an "automatic logoff" (Let's face it, that's what we're really trying to accomplish here.) The problem that we run into is clustering. In order to cluster a site and use client variables, setdomaincookies must be set to yes. This causes 2 issues:

1. The cookies are set from the cfapplication tag which makes the setclientcookies to "no" logic null and void

2. All of our apps have dev and stage environments and in most cases, the environments are dev.myapp.com, stage.myapp.com and www.myapp.com. This causes a couple of copies of the cookies with different values to be created and accessed via the browsers. For example, myapp.com cookies are accessed and used when browsing to dev.myapp.com.

Do you know of a way around using the setdomaincookies or a way to force a browser to ignore all of the myapp.com cookies and only use the cookies for the sub domain (i.e. dev.myapp.com)? Another option may be to avoid creating myapp.com cookies? Any ideas there?

2 Comments

Ben, I have not been able to get this procedure to work in Coldfusion 10(it worked fine for the last 7 years with CF 7 & 8). In CF10, CFID & CFToken gets set with an expires time based on the "Session Cookie Settings" in the CF10 Administrator (under Memory Variables) (minimum setting is 2 minutes) & that setting cannot be disabled & it cannot be overridden. However, that setting can be overridden by other cookies, such as <cfcookie name="myCookie" value="hello world">, which gets set with expires="When I close my browser". Any ideas would be greatly appreciated, thanks.

2 Comments

OK, finally figured it out through trial and error. Did not find (complete)
documentation that explained how to do this. Set this in the Application.cfc
(beneath the <cfcomponent> tag) <cfset THIS.sessioncookie.timeout = "-1"
> and this will cause the cfid & cftoken cookies "Expires" attribute to be set
to "When I close my browser". Note: if you are using a shared server & "Disable
updating Coldfusion internal cookie using Coldfusion tags/functions" is checked,
you can override it by putting <cfset
THIS.sessioncookie.disableupdate=false> in the Application.cfc, which gives
control back to you, otherwise cfid & cftoke will expire at whatever interval is
set in CF Admin. In OnSessionStart, you can set the cookies like this:
<cfcookie name="cfid" value="#SESSION.CFID#"> <cfcookie name="cftoken"
value="#SESSION.CFToken#"> or like this: <cfset Cookie.cfid =
SESSION.CFID> <cfset Cookie.cftoken = SESSION.CFToken>, just make sure
you don't use the expires attribute when setting them.

1 Comments

Ben, thanks for your suggestion with the "ping" to keep a session alive. I tried to implement your solution with CF10:

<cfheader name="content-length" value="0" />
<cfcontent type="image/gif" variable="#ArrayNew( 1 )#" />

but I get the following error in application.log:

Attribute validation error for tab cfcontent.
coldfusion.runtime.Array is not a supported variable type.
The variable is expected to contain binary data.

I can use the "file" attribut with an empty gif file, but that adds extra overhead to the ping mechanism. This is of no real concern in my use case (intranet application with very few concurrent users), but can you still suggest an alternative approach to minimise the impact of the ping?

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel