The usual technique is to use mod_rewrite as suggested by Josh. This isn't perfect but removes the majority of hot-linking so would probably do the job you are looking for.
There is a more complex way that almost guarantees no hot-linking: make sure each visitor needs to use a different URL to get the same image:
- Place the image files where the web server can not ordinarily see them (so they can not be directly addressed by a simple URL)
- When you send out a page that uses an image, generate a random code to refer to this instance of this image (for instance use your scripting language's UUID functions to return a random UUID, or use a salted hash of the current time in milliseconds since epoc), and drop an row into a database that contains this code and the name+location of the image file and the current time
- Return to the calling browser an image tag or link that is a call to a script with this code in the URL
- When the browser makes a request to this script, lookup the right image in the DB using the code and return its data
- Regularly delete the rows from the database if they are older than an hour or so. This means the link to the image is only valid for an hour. For inline images you can make this shorter - say only keeping them around for a minute or two reducing the time the link to the image is valid.
This had a couple of major disadvantages though:
- You page must be scripted, turning what could be a plain static HTML page into something that requires a chunky script processor like PHP to be run
- Each request for one of the protected images is also a scripted response instead of a simple request for a static object. You can get around this by using filesystem links instead of database entries if your OS and filesystem support this - just create a link to the image in a know spot on the filesystem that the web server can see, and send a normal URL to that - so you send out an image tag that refers to
http://yourhost/images/random-code-blah-123456.jpg. Then you just need to clear out the links regularly instead of deleting database rows. If you don't want to (or can't) deal with links, just copy the file - but this imparts a lot of extra I/O load for each page request.
- You are hitting your database more then you would otherwise need to. The symlink/hardlink/file-copy method gets around this though.
- The images are available for hot-linking during the time-out period. You can work around this by using part of the caller's IP address as part of the code for the file. Don't use all the IP address though as many users are stuck behind proxies that may make their address change randomly (you should be safe to use the first half of the address though unless the viewer is using a global anonymising proxy network like Tor). This only works for the fully scripted approach though.
If this all sounds like far too much work, you are probably right. Go with the mod_rewrite option - it is generally effective enough, imposes far far less load on your server(s) and is much less work to setup.