# Global configuration; see https://securityheaders.com Header set X-UA-Compatible "IE=edge" Header set X-Frame-Options "sameorigin" Header set X-Content-Type-Options "nosniff" Header set X-Xss-Protection "1; mode=block" Header set Referrer-Policy "no-referrer-when-downgrade" ServerTokens prod ServerSignature off AddDefaultCharset utf-8 UseCanonicalName on LogLevel warn AddOutputFilterByType deflate image/svg+xml AddType application/font-woff2 .woff2 TraceEnable off Protocols h2 h2c http/1.1 # Set up caching directives for infrequently changed files ExpiresActive On ExpiresByType application/font-woff "access plus 1 month" ExpiresByType application/font-woff2 "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/svg+xml "access plus 1 month" ExpiresByType image/vnd.microsoft.icon "access plus 1 month" ExpiresByType image/x-icon "access plus 1 month" ExpiresByType text/css "access plus 1 month" # These lines are a workaround for an Apache bug that prevents mod_deflate, etags, and ExpiresByType working at the same time. # This is probably still broken in 18.04. See https://stackoverflow.com/questions/896974/apache-is-not-sending-304-response-if-mod-deflate-and-addoutputfilterbytype-is FileETag All RequestHeader edit "If-None-Match" "^\"(.*)-gzip\"$" "\"$1\"" Header edit "ETag" "^\"(.*[^g][^z][^i][^p])\"$" "\"$1-gzip\"" # SSL hardening; see https://mozilla.github.io/server-side-tls/ssl-config-generator/ SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 SSLHonorCipherOrder on SSLCompression off SSLSessionTickets off SSLStaplingCache shmcb:/var/run/ocsp(128000) # SSL Stapling should be off for testing to prevent errors in log files, and on for live SSLUseStapling off SSLStaplingResponderTimeout 5 SSLStaplingReturnResponderErrors off Define domain standardebooks.test ServerName ${domain} ServerAlias www.${domain} RedirectPermanent / https://${domain}/ ServerName ${domain} ServerAlias www.${domain} DocumentRoot /standardebooks.org/web/www ErrorDocument 404 /404 ErrorLog /var/log/local/www-error.log RewriteEngine on SSLEngine on SSLCertificateFile /standardebooks.org/web/config/ssl/${domain}.crt SSLCertificateKeyFile /standardebooks.org/web/config/ssl/${domain}.key Header always set Strict-Transport-Security "max-age=15768000" Header set Content-Security-Policy "default-src 'self';" # Log downloads SetEnvIf Request_URI "\.epub$" logdownload SetEnvIf Request_URI "\.kepub.epub$" logdownload SetEnvIf Request_URI "\.azw3$" logdownload CustomLog /var/log/local/downloads.log "%h [%{%Y-%m-%d %H:%M:%S %Z}t] \"%r\" %>s %b" env=logdownload DirectorySlash Off # Disable .htaccess files AllowOverride none # Disable unneeded options Options none # Allow access to www/ Require all granted # Pass HTTP Authorization headers to PHP-FPM CGIPassAuth on AddType application/epub+zip .epub AddType application/x-mobi8-ebook .azw3 # Serve distributables using the "download" dialog instead of opening in-browser # Note: the trailing e in the Header directive is required SetEnvIf Request_URI ^/ebooks/.+?/downloads/(.+)$ FILENAME=$1 Header set "Content-Disposition" "attachment; filename=%{FILENAME}e" DirectoryIndex index.xml # We explicitly set the content-type for items in the /vocab/ directory, because Apache doesn't set it for us, # and we need a content-type header when using the "nosniff" header. See https://bugzilla.mozilla.org/show_bug.cgi?id=1547076 Header set Content-Type "text/plain" # Enable HTTP CORS so that browser-based readers like Readium can access opds and ebooks # See https://github.com/standardebooks/tools/issues/2 Header set Access-Control-Allow-Origin "*" # We use a different CSP policy for single-page files because our default one doesn't allow inline images or CSS Header set Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';" # Remove www from requests RewriteCond %{HTTP_HOST} ^www\.(.+) [NC] RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L] # PHP-FPM configuration # See https://serverfault.com/questions/450628/apache-2-4-php-fpm-proxypassmatch/510784 # Forward all PHP requests to the php-fpm pool for this domain. SetHandler "proxy:unix:/run/php/${domain}.sock|fcgi://${domain}" Header set Cache-Control "no-store" # Set some proxy properties. ProxySet connectiontimeout=5 timeout=240 # In case of 404, serve the 404 page specified by ErrorDocument, not the default FPM error page. # Note that we can't use `ProxyErrorOverride on` because that catches ALL 4xx and 5xx HTTP headers # and serves the default Apache page for them. RewriteCond %{REQUEST_FILENAME} \.php$ RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-f RewriteRule (.*) - [H=text/html] # Received: /filename.php and /filename.php exists in filesystem; Result: 301 redirect to /filename and restart request RewriteCond %{REQUEST_FILENAME} \.php$ RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} -f RewriteRule ^/(.+)\.php$ /$1 [R=301,L] # Received: /filename and /filename.php exists in filesystem; Result: change /filename to /filename.php and continue processing RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-f RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-d RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI}.php -f RewriteRule ^(.+)$ $1.php [QSA] # End PHP-FPM configuration # Received: /filename and /filename.xml exists in filesystem; Result: rewrite to /filename.xml RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}.xml -f RewriteRule (.*) $1.xml # Remove trailing slashes RewriteRule ^/(.+?)/$ /$1 [R=301,L] # Redirect ToC of XHTML representation of books RewriteRule ^/ebooks/(.+?)/text$ /ebooks/$1/toc.xhtml [L] # Received: /filename and /filename.xhtml exists in filesystem; Result: rewrite to /filename.xhtml RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}.xhtml -f RewriteRule (.*) $1.xhtml # Redirect index pages RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}/index.php -f RewriteRule (.*) /$1/index.php RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}/index.xml -f RewriteRule (.*) /$1/index.xml # Remove newline characters inserted by accident in some email clients RewriteRule ^(.*)\r\n[\ ]?(.*)$ $1$2 [R=301,N] RewriteRule ^(.*)/r/n[\ ]?(.*)$ $1$2 [R=301,N] RewriteRule ^(.*)/[rn]$ $1 [R=301,N] # Favicon rewrites RewriteRule ^/(apple-touch|android-chrome|favicon|mstile|safari-pinned|browserconfig|manifest)([^/]+)$ /images/favicons/$1$2 [L] # Redirect tools RewriteRule ^/tools$ https://github.com/standardebooks/tools [R=302,L] # Redirect latest version of the manual RewriteRule ^/manual/latest(.*) /manual/index.php?url=$1 [L] # List of specific URL rewrites RewriteRule ^/ebooks/aristotle/the-nicomachean-ethics(.+)$ /ebooks/aristotle/nicomachean-ethics$1 [R=301,L] RewriteRule ^/ebooks/sir-arthur-conan-doyle(.+)$ /ebooks/arthur-conan-doyle$1 [R=301,L] RewriteRule ^/alices-adventures-in-wonderland.+$ /ebooks/lewis-carroll/alices-adventures-in-wonderland/$1 [R=301,L] RewriteRule ^/ebooks/philip-k-dick/short-stories(.+)$ /ebooks/philip-k-dick/short-fiction$1 [R=301,L] RewriteRule ^/ebooks/benjamin-disraeli/sibyl(.+)$ /ebooks/benjamin-disraeli/sybil$1 [R=301,L] RewriteRule ^/ebooks/lewis-carroll/alices-adventures-in-wonderland/downloads/(.+)$ /ebooks/lewis-carroll/alices-adventures-in-wonderland/john-tenniel/downloads/$1 [R=301,L] RewriteRule ^/ebooks/lewis-carroll/through-the-looking-glass/downloads/(.+)$ /ebooks/lewis-carroll/through-the-looking-glass/john-tenniel/downloads/$1 [R=301,L] RewriteRule ^/ebooks/nikolai-gogol/short-fiction/claud-field_isabel-f-hapgood_vizetelly-and-company$ /ebooks/nikolai-gogol/short-fiction/claud-field_isabel-f-hapgood_vizetelly-and-company_george-tolstoy [R=301,L] RewriteRule ^/ebooks/nikolai-gogol/short-fiction/claud-field_isabel-f-hapgood_vizetelly-and-company/(.+?)$ /ebooks/nikolai-gogol/short-fiction/claud-field_isabel-f-hapgood_vizetelly-and-company_george-tolstoy/$1 [R=301,L] RewriteRule ^/ebooks/h-g-wells/tales-of-space-and-time(.+)$ /ebooks/h-g-wells/short-fiction$1 [R=301,L] RewriteRule ^/ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam/edward-fitzgerald/edmund-dulac(.*?)$ /ebooks/omar-khayyam/the-rubaiyat-of-omar-khayyam/edward-fitzgerald$1 [R=301,L] RewriteRule ^/ebooks/alfred-lord-tennyson/idylls-of-the-king/gustave-dore(.*?)$ /ebooks/alfred-lord-tennyson/idylls-of-the-king$1 [R=301,L] RewriteRule ^/ebooks/w-w-jacobs/the-lady-of-the-barge/maurice-greiffenhagen(.*?)$ /ebooks/w-w-jacobs/the-lady-of-the-barge$1 [R=301,L] RewriteRule ^/ebooks/robert-louis-stevenson/treasure-island/milo-winter(.*?)$ /ebooks/robert-louis-stevenson/treasure-island$1 [R=301,L] RewriteRule ^/ebooks/edgar-rice-burroughs/a-princess-of-mars/frank-e-schoonover(.*?)$ /ebooks/edgar-rice-burroughs/a-princess-of-mars$1 [R=301,L] RewriteRule ^/ebooks/abraham-merritt/the-moon-pool(.*?)$ /ebooks/a-merritt/the-moon-pool$1 [R=301,L] RewriteRule ^/ebooks/leo-tolstoy/a-confession/aylmer-maude_louise-maude(.*?)$ /ebooks/leo-tolstoy/a-confession/aylmer-maude$1 [R=301,L] RewriteRule ^/ebooks/f-marion-crawford/khaled-a-tale-of-arabia(.*?)$ /ebooks/f-marion-crawford/khaled$1 [R=301,L] # Redirect cover images with caching sha's to the root image # We do this because some sites like Google cache the cover image path, so changing it results in lots of 404s RewriteRule ^/images/covers/(.+?)\-[a-z0-9]{8}\-(cover|hero)(@2x)?\.(jpg|avif)$ /images/covers/$1-$2$3.$4 RewriteRule ^/ebooks/([^\./]+?)$ /ebooks/author.php?url-path=$1 [QSA] RewriteRule ^/tags/([^\./]+?)$ /ebooks/index.php?tags[]=$1 [QSA] RewriteRule ^/collections/([^\./]+?)$ /ebooks/index.php?collection=$1 [QSA] # Prevent this rule from firing if we're getting a distribution file RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/downloads/.+$ RewriteCond %{REQUEST_FILENAME} !^/ebooks/.+?/text.*$ RewriteRule ^/ebooks/([^\.]+?)$ /ebooks/ebook.php?url-path=$1 # If we ask for /opds/all?query=xyz, rewrite that to the search page. RewriteCond %{QUERY_STRING} ^query= RewriteRule ^/opds/all.xml$ /opds/search.php [QSA]