try using a multiplicative "bias" instead of additive:
shadowCoordinate.z *= 0.98; If you're doing the sampling yourself rather than using the shadow comparator interpolating the shadow map helps reduce acne a lot.
There shouldn't be any shadow visible behind the object as the light should not affect it with the light being completely occluded by the object itself.
Global ambient light should not be affected by shadows, shadows should only cuts off the light that is casting the shadow not the other lights.
vec3 output_color = ambient * material_diffuse; foreach(light){ float shadow_masking = light.CalculateShadow(); output_color += light.Calculate(material_diffuse, material_specular) * shadow_masking; } Here's 3 colored point lights using cubemap shadow casting and 1 directional. Shadow maps are rendered front-face (the usual back-face culling). Single-pass forward-shading. And a multiplicative "bias" of 1.02 applied to the shadow depth. Shadow map depth is interpolated. The columns grooves are actually modeled (not normal maps) to stress test self-shadowing and shadow acne issues. The 3 small spheres are the point light sources. Rendered on an nVidia GTX 560M. The minimum requirement is GLSL 1.1 (OpenGL 2.0). 
Edit: Adding pseudo-code of the internals of sample2DShadow vs sampler2D
sample2DShadow:
float A = Nearest(tex, floor(coord.xy + vec2(0, 0))).r < coord.z ? 1.0 : 0.0; float B = Nearest(tex, floor(coord.xy + vec2(1, 0))).r < coord.z ? 1.0 : 0.0; float C = Nearest(tex, floor(coord.xy + vec2(0, 1))).r < coord.z ? 1.0 : 0.0; float D = Nearest(tex, floor(coord.xy + vec2(1, 1))).r < coord.z ? 1.0 : 0.0; return Lerp( Lerp(A, B, fract(coord.x)), Lerp(C, D, fract(coord.x)), fract(coord.y)); sampler2D on depth texture:
vec4 A = Nearest(tex, floor(coord.xy + vec2(0, 0))).rrrr; vec4 B = Nearest(tex, floor(coord.xy + vec2(1, 0))).rrrr; vec4 C = Nearest(tex, floor(coord.xy + vec2(0, 1))).rrrr; vec4 D = Nearest(tex, floor(coord.xy + vec2(1, 1))).rrrr; return Lerp( Lerp(A, B, fract(coord.x)), Lerp(C, D, fract(coord.x)), fract(coord.y)); Both types of samplers do 1 bilinear interpolation, but the sampler2DShadow does it on the boolean result.
