mirror of
https://github.com/standardebooks/web.git
synced 2025-07-16 11:26:37 -04:00
321 lines
16 KiB
Text
321 lines
16 KiB
Text
# Global configuration; see https://securityheaders.com
|
|
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
|
|
AddType image/avif .avif
|
|
AddType font/woff2 .woff2
|
|
AddOutputFilterByType deflate image/svg+xml
|
|
AddOutputFilterByType deflate application/xhtml+xml
|
|
AddOutputFilterByType deflate image/vnd.microsoft.icon image/x-icon
|
|
TraceEnable off
|
|
Protocols h2 h2c http/1.1
|
|
|
|
# Set up caching directives for infrequently changed files
|
|
ExpiresActive on
|
|
ExpiresByType application/javascript "access plus 6 months"
|
|
ExpiresByType text/javascript "access plus 6 months"
|
|
ExpiresByType font/woff2 "access plus 6 months"
|
|
ExpiresByType image/avif "access plus 6 months"
|
|
ExpiresByType image/gif "access plus 6 months"
|
|
ExpiresByType image/jpeg "access plus 6 months"
|
|
ExpiresByType image/png "access plus 6 months"
|
|
ExpiresByType image/svg+xml "access plus 6 months"
|
|
ExpiresByType image/vnd.microsoft.icon "access plus 6 months"
|
|
ExpiresByType image/x-icon "access plus 6 months"
|
|
ExpiresByType text/css "access plus 6 months"
|
|
|
|
# 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
|
|
# Can possibly be fixed in Ubuntu 22.04 using https://httpd.apache.org/docs/trunk/mod/mod_deflate.html#deflatealteretag
|
|
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
|
|
Define webroot /standardebooks.org/web
|
|
|
|
<VirtualHost *:80>
|
|
ServerName ${domain}
|
|
ServerAlias www.${domain}
|
|
RedirectPermanent / https://${domain}/
|
|
</VirtualHost>
|
|
|
|
<VirtualHost *:443>
|
|
ServerName ${domain}
|
|
ServerAlias www.${domain}
|
|
DocumentRoot ${webroot}/www
|
|
ErrorDocument 404 /404
|
|
ErrorDocument 451 /451
|
|
ErrorLog /var/log/local/www-error.log
|
|
DirectorySlash Off
|
|
RewriteEngine on
|
|
|
|
SSLEngine on
|
|
SSLCertificateFile ${webroot}/config/ssl/${domain}.crt
|
|
SSLCertificateKeyFile ${webroot}/config/ssl/${domain}.key
|
|
Header always set Strict-Transport-Security "max-age=15768000"
|
|
Header set Content-Security-Policy "default-src 'self';"
|
|
|
|
<Directory "${webroot}/www/">
|
|
# 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
|
|
</Directory>
|
|
|
|
AddType application/x-mobi8-ebook .azw3
|
|
|
|
<LocationMatch "^/ebooks/.+?/downloads/.+$">
|
|
# Serve distributables using the "download" dialog instead of opening in-browser
|
|
# Note: the trailing e in the Header directive is required
|
|
# In modern browsers this is handled with the a@download attribute, we keep this here for compatibility
|
|
SetEnvIf Request_URI ^/ebooks/.+?/downloads/(.+)$ FILENAME=$1
|
|
Header set Content-Disposition "attachment; filename=%{FILENAME}e"
|
|
</LocationMatch>
|
|
|
|
# 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
|
|
<LocationMatch "^/vocab/.+$">
|
|
Header set Content-Type "text/plain"
|
|
</LocationMatch>
|
|
|
|
# Enable HTTP CORS so that browser-based readers like Readium can access opds and ebooks
|
|
# Allow fonts for newsletter emails
|
|
# See https://github.com/standardebooks/tools/issues/2
|
|
<LocationMatch "/(ebooks|feeds/opds|fonts)">
|
|
Header set Access-Control-Allow-Origin "*"
|
|
</LocationMatch>
|
|
|
|
# We use a different CSP policy for single-page files because our default one doesn't allow inline images or CSS
|
|
<LocationMatch "/text/single-page$">
|
|
Header set Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';"
|
|
</LocationMatch>
|
|
|
|
# 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
|
|
|
|
# Required for FPM to receive POST data sent with Transfer-Encoding: chunked
|
|
SetEnv proxy-sendcl 1
|
|
|
|
# Forward all PHP requests to the php-fpm pool for this domain.
|
|
<FilesMatch "\.php$">
|
|
SetHandler "proxy:unix:///run/php/${domain}.sock|fcgi://${domain}"
|
|
Header set Cache-Control "no-store"
|
|
</FilesMatch>
|
|
|
|
# Set some proxy properties.
|
|
<Proxy "fcgi://${domain}">
|
|
ProxySet connectiontimeout=5 timeout=240
|
|
</Proxy>
|
|
|
|
# In RewriteCond, RewriteRule gets evaluated BEFORE RewriteCond, so $1 refers to the first
|
|
# match in RewriteRule
|
|
# Rewrite POST /some/url -> POST /some/url/post.php
|
|
RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^(post|delete|put)$/"
|
|
RewriteCond %{DOCUMENT_ROOT}/$1/%1.php -f
|
|
RewriteRule ^([^\.]+)$ $1/%1.php [L]
|
|
|
|
# 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]
|
|
|
|
# Rewrite ebook downloads
|
|
RewriteRule ^/ebooks/(.+?)/download$ /ebooks/download.php?url-path=$1 [QSA]
|
|
|
|
# Work around some naughty hotlinkers
|
|
RewriteCond %{HTTP_REFERER} !^$
|
|
RewriteCond %{HTTP_REFERER} ^https?://.*medialibrary.it [NC]
|
|
RewriteRule ^(/ebooks/.+/downloads/|/images/covers/) /images/do-not-hotlink.jpg [R=301,L,NC]
|
|
|
|
# List of specific URL rewrites
|
|
RewriteRule ^/contribute/accepted-ebooks/? /contribute/collections-policy [R=301,L]
|
|
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 ^/ebooks/benjamin-disraeli/sibyl(/?$|/.+?$) /ebooks/benjamin-disraeli/sybil$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$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]
|
|
RewriteRule ^/ebooks/h-rider-haggard/she/maurice-greiffenhagen_charles-h-m-kerr(/?$|/.+?$) /ebooks/h-rider-haggard/she$1 [R=301,L]
|
|
RewriteRule ^/ebooks/ivan-bunin/short-fiction/s-s-koteliansky_d-h-lawrence_leonard-woolf_bernard-guilbert-guerney_the-russian-review(/?$|/.+?$) /ebooks/ivan-bunin/short-fiction/s-s-koteliansky_d-h-lawrence_leonard-woolf_bernard-guilbert-guerney_the-russian-review_natalie-a-duddington$1 [R=301,L]
|
|
RewriteRule ^/ebooks/henry-david-thoreau/a-yankee-in-canada(/?$|/.+?$)$ /ebooks/henry-david-thoreau/essays$1 [R=301,L]
|
|
RewriteRule ^/ebooks/mark-twain/the-autobiography(/?$|/.+?$)$ /ebooks/mark-twain/the-autobiography-of-mark-twain$1 [R=301,L]
|
|
RewriteRule ^/ebooks/e-nesbit/the-enchanted-castle/h-r-millar(/?$|/.+?$) /ebooks/e-nesbit/the-enchanted-castle$1 [R=301,L]
|
|
RewriteRule ^/ebooks/epictetus/the-enchiridion/elizabeth-carter(/?$|/.+?$) /ebooks/epictetus/short-works/george-long$1 [R=301,L]
|
|
RewriteRule ^/ebooks/taras-shevchenko/poetry/alexander-jardine-hunter_ethel-lilian-voynich_paul-selver_florence-randal-livesay(/?$|/.+?$) /ebooks/taras-shevchenko/poetry/alexander-jardine-hunter_ethel-voynich_paul-selver_florence-randal-livesay$1 [R=301,L]
|
|
RewriteRule ^/ebooks/nikolai-gogol/dead-souls/d-j-hogarth(/?$|/.+?$) /ebooks/nikolai-gogol/dead-souls/c-j-hogarth$1 [R=301,L]
|
|
RewriteRule ^/ebooks/sigfrid-siwertz/downstream/macmillan-of-canada(/?$|/.+?$) /ebooks/sigfrid-siwertz/downstream/e-classen$1 [R=301,L]
|
|
RewriteRule ^/ebooks/samuel-richardson/clarissa-harlowe(/?$|/.+?$) /ebooks/samuel-richardson/clarissa$1 [R=301,L]
|
|
|
|
# Rewrite ebooks taken down due to copyright clarifications
|
|
RewriteRule ^/ebooks/jules-verne/twenty-thousand-leagues-under-the-seas/f-p-walter.* - [R=451,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 ^/ebooks/([^\./]+?)/downloads$ /bulk-downloads/get.php?author=$1 [QSA]
|
|
RewriteRule ^/ebooks/([^\./]+?)/feeds$ /feeds/get.php?author=$1 [QSA]
|
|
RewriteRule ^/subjects/([^\./]+?)$ /ebooks/index.php?tags[]=$1 [QSA]
|
|
RewriteRule ^/collections/([^\./]+?)$ /collections/get.php?collection=$1 [QSA]
|
|
RewriteRule ^/collections/([^/]+?)/downloads$ /bulk-downloads/get.php?collection=$1
|
|
RewriteRule ^/collections/([^/]+?)/feeds$ /feeds/get.php?collection=$1
|
|
|
|
# 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
|
|
|
|
# Newsletter
|
|
RewriteRule ^/newsletter$ /newsletter/subscriptions/new.php [L]
|
|
RewriteRule ^/newsletter/subscriptions/([^/\.]+?)$ /newsletter/subscriptions/get.php?uuid=$1 [L]
|
|
RewriteRule ^/newsletter/subscriptions/([^/\.]+?)/(confirm|delete)$ /newsletter/subscriptions/$2.php?uuid=$1 [L]
|
|
|
|
# Polls
|
|
RewriteRule ^/polls/([^/\.]+)$ /polls/get.php?pollurlname=$1 [L]
|
|
RewriteRule ^/polls/([^/\.]+)/votes/new$ /polls/votes/new.php?pollurlname=$1 [L]
|
|
RewriteRule ^/polls/([^/\.]+)/votes/([0-9]+)$ /polls/votes/get.php?pollurlname=$1&userid=$2 [L]
|
|
|
|
RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^get$/"
|
|
RewriteRule ^/polls/([^/\.]+)/votes$ /polls/votes/index.php?pollurlname=$1 [L]
|
|
|
|
RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^post$/"
|
|
RewriteRule ^/polls/([^/\.]+)/votes$ /polls/votes/post.php?pollurlname=$1 [L]
|
|
|
|
# Rewrite rules for feeds eeds
|
|
# Redirect old feed URLs
|
|
RewriteRule ^/(opds|rss|atom)(.*)$ /feeds/$1$2 [R=301,L]
|
|
|
|
# If we ask for /opds/all?query=xyz, rewrite that to the search page.
|
|
RewriteCond %{QUERY_STRING} \bquery=
|
|
RewriteRule ^/feeds/(opds|atom|rss)/all.xml$ /feeds/$1/search.php [QSA]
|
|
|
|
RewriteRule ^/feeds/(atom|rss)/([^/\.]+)$ /feeds/collection.php?type=$1&class=$2
|
|
|
|
RewriteRule ^/feeds/(.+\.xml)$ /feeds/download.php?path=$1
|
|
|
|
# Rewrite rules for bulk downloads
|
|
RewriteRule ^/bulk-downloads/(.+\.zip)$ /bulk-downloads/download.php?path=$1
|
|
RewriteRule ^/bulk-downloads/([^/\.]+)$ /bulk-downloads/collection.php?class=$1
|
|
|
|
# Rewrite rules for cover art
|
|
RewriteRule ^/artworks/([^\./]+?)$ /artists/get.php?artist-url-name=$1 [L]
|
|
|
|
RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^get$/"
|
|
RewriteRule ^/artworks/([^/\.]+)/([^/\.]+)$ /artworks/get.php?artist-url-name=$1&artwork-url-name=$2 [L]
|
|
|
|
RewriteCond expr "tolower(%{REQUEST_METHOD}) =~ /^post$/"
|
|
RewriteRule ^/artworks/([^/\.]+)/([^/\.]+)$ /artworks/post.php?artist-url-name=$1&artwork-url-name=$2 [L]
|
|
|
|
RewriteRule ^/artworks/([^/\.]+)/([^/\.]+)/edit$ /artworks/edit.php?artist-url-name=$1&artwork-url-name=$2 [L]
|
|
|
|
# Specific config for /ebooks/<author>/<ebook>/downloads
|
|
<DirectoryMatch "^${webroot}/www/ebooks/.+">
|
|
# Both directives are required
|
|
XSendFile on
|
|
XSendFilePath ${webroot}/www/ebooks
|
|
</DirectoryMatch>
|
|
|
|
# Specific config for /bulk-downloads
|
|
<DirectoryMatch "^${webroot}/www/bulk-downloads/">
|
|
# Both directives are required
|
|
XSendFile on
|
|
XSendFilePath ${webroot}/www/bulk-downloads
|
|
</DirectoryMatch>
|
|
|
|
# Specific config for /feeds
|
|
<DirectoryMatch "^${webroot}/www/feeds/">
|
|
# This must be defined at the top level /feeds/ directory
|
|
# Both directives are required
|
|
XSendFile on
|
|
XSendFilePath ${webroot}/www/feeds
|
|
</DirectoryMatch>
|
|
</VirtualHost>
|