It seems I have tracked the origin of the problem.
When a browser receives an invalid ZIP file, it is actually a GZIP-compressed ZIP file. gunzip it, and then you can unzip it too.
What causes it is of course mod_deflate, since it's the only part of the site that may cause gzip-compression. It is intended to save bandwidth.
So why is it broken? I suspect it's a problem in the browsers. They claim they can decompress stuff in transmission, but when it's a downloadable file, they forget to decompress it before saving. Some browsers.
So how to fix it? DeHackEd, help!
This is my /etc/apache2/mods-available/deflate.conf:
<Location />
  # Insert filter
  SetOutputFilter DEFLATE
  SetInputFilter DEFLATE
  # Netscape 4.x has some problems...
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  # Netscape 4.06-4.08 have some more problems
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  # MSIE masquerades as Netscape, but it is fine
  # BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
  # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
  # the above regex won't work. You can use the following
  # workaround to get the desired effect:
  BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
  # Don't compress images
  SetEnvIfNoCase Request_URI \
  \.(?:gif|jpe?g|png)$ no-gzip dont-vary
  # Make sure proxies don't deliver the wrong content
  Header append Vary User-Agent env=!dont-vary
</Location>
DeflateCompressionLevel 2
For comparison, this was my /etc/apache/gzip.conf (mod_gzip, which I used in Apache 1.3, is not available in Apache 2.0, which I use now):
  <IfModule mod_gzip.c>
  mod_gzip_on                   Yes
  mod_gzip_can_negotiate        No
  mod_gzip_static_suffix        .gz
  AddEncoding              gzip .gz
  mod_gzip_update_static        No
  mod_gzip_command_version      '/mod_gzip_status'
mod_gzip_temp_dir             /tmp
  mod_gzip_keep_workfiles       No
  mod_gzip_minimum_file_size    500
  mod_gzip_maximum_file_size    800000
  mod_gzip_maximum_inmem_size   200000
  mod_gzip_min_http             1000
  mod_gzip_handle_methods        GET POST
  mod_gzip_item_exclude         reqheader  "User-agent: Mozilla/4.0[678]"
  mod_gzip_item_include         file       \.htm$
  mod_gzip_item_include         file       \.html$
  mod_gzip_item_include         file       \.js$
  mod_gzip_item_include         file       \.css$
  mod_gzip_item_include         file       \.php.*
  mod_gzip_item_include         file       \.xml$
  mod_gzip_item_include         file       \.pl$
  mod_gzip_item_include         handler    ^cgi-script$
  mod_gzip_item_include         mime       ^text/.*
  mod_gzip_item_include         mime       ^httpd/unix-directory$
  mod_gzip_item_include         mime       ^application/x-javascript$
  mod_gzip_item_exclude         file    pushrefresh
  mod_gzip_item_exclude         file    spamref
  mod_gzip_item_exclude         mime       ^image/
  mod_gzip_dechunk              Yes
  LogFormat                     "%h %l %u %t \"%V %r\" %<s %b mod_gzip: %{mod_gzip_result}n In:%{mod_gzip_input_size}n -< Out:%{mod_gzip_output_size}n = %{mod_gzip_compression_ratio}n pct." common_with_mod_gzip_info2
  mod_gzip_add_header_count     Yes
  mod_gzip_send_vary            Yes
  </IfModule>