This file combines ErrorDocument to Handlers and Downloads for a PHP site with pages, a front-end build in /assets/, files to save in /downloads/ and user files in /uploads/:
# /var/www/example/.htaccess: pages, assets, downloads and uploads on one site
# Needs: mod_dir, mod_mime, mod_headers; AllowOverride FileInfo Indexes Options=Indexes
ErrorDocument 404 /errors/404.php
ErrorDocument 403 /errors/403.html
DirectoryIndex index.php index.html
Options -Indexes
AddDefaultCharset utf-8
AddType text/javascript .cjs
AddType application/json .map
AddCharset utf-8 .css .js .mjs .cjs .map .json .webmanifest .svg
RemoveType .gz .br
RemoveLanguage .br
AddEncoding gzip .gz
AddEncoding br .br
<If "%{REQUEST_URI} =~ m#^/downloads/.+\.(pdf|csv|txt)$#">
ForceType application/octet-stream
Header set Content-Disposition "attachment"
</If>
<If "%{REQUEST_URI} =~ m#^/uploads/.+\.(php|phtml|phar|pl|py|cgi|sh)$#">
SetHandler default-handler
ForceType text/plain
</If>Lines 3 to 6 come from ErrorDocument and DirectoryIndex. Line 7 labels HTML and plain text UTF-8; line 10 does the same for text assets, which AddDefaultCharset never touches. Lines 8 and 9 fill the type gaps of MIME Types, and lines 11 to 14 make .gz and .br encodings, safe only because this site serves no archives. .htaccess cannot hold <Directory>, so the two <If> blocks scope the switches of Handlers and Downloads by path. Six requests show each URL, its status and its Content- headers:
/files/ 403 Type: text/html; charset=utf-8 /assets/legacy.cjs 200 Type: text/javascript; charset=utf-8 /assets/app.js.map 200 Type: application/json; charset=utf-8 /assets/style.css.br 200 Type: text/css; charset=utf-8 Encoding: br /downloads/prices.csv 200 Disposition: attachment Type: application/octet-stream /uploads/hello.php 200 Type: text/plain; charset=utf-8
With server access, move these lines into the vhost's <Directory> blocks (Moving Rules to Config). Assets Directory Block adds CORS and caching for an assets directory.