/
.htaccess
179 lines (153 loc) · 7.41 KB
/
.htaccess
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Enable content negotiation
Options +MultiViews
# Allow PHP to match negotiated response for any request.
# https://stackoverflow.com/a/24598848/503410
<FilesMatch ".+\.php$">
MultiviewsMatch Any
</FilesMatch>
DirectoryIndex index
# Note: Be careful to avoid compounding errors due to failed negotiation (406)
# This is particularly likely for 404 when HTML is not accepted (since error
# document is an HTML document) so it can not be type-negotiated.
<If "%{HTTP_ACCEPT} =~ m#application/xhtml\+xml\s*(?:,|$)#i">
ErrorDocument 400 /400.xhtml
ErrorDocument 401 /401.xhtml
ErrorDocument 403 /403.xhtml
ErrorDocument 404 /404.xhtml
ErrorDocument 500 /500.xhtml
</If>
<ElseIf "%{HTTP_ACCEPT} =~ m#text/html\s*(?:,|$)#i">
ErrorDocument 400 /400.html
ErrorDocument 401 /401.html
ErrorDocument 403 /403.html
ErrorDocument 404 /404.html
ErrorDocument 500 /500.html
</ElseIf>
<Else>
# Type negotiation would fail. Name non-negotiated file explicitly.
ErrorDocument 400 /400.html.html
ErrorDocument 401 /401.html.html
ErrorDocument 403 /403.html.html
ErrorDocument 404 /404.html.html
ErrorDocument 500 /500.html.html
</Else>
# mod_auto_index options
HeaderName README-files.txt
IndexOptions +XHTML
# Set common security/compatibility headers
Header set Content-Security-Policy: "default-src 'self'; form-action 'self'; block-all-mixed-content; report-uri /mail/csp"
Header set X-Content-Type-Options: "nosniff"
Header set X-XSS-Protection: "1; mode=block"
Header set X-Frame-Options: "sameorigin"
Header set X-UA-Compatible: "IE=Edge"
# FollowSymLinks is required for rewriting
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
# Note: R without L in RewriteRules for single redirect to new URL with HTTPS
# Note: QSA is not needed when replacement has no query string
# Rewrite old blog post URLs to their new locations
RewriteRule "^inquiry/sdlblitspeed/sdlblitspeed\.php$" "/bits/2007/08/01/sdl-blit-speed-comparison/" [DPI,R=permanent]
RewriteRule "^inquiry/sdlblitspeed/(.*)$" "/bits/2007/08/01/sdl-blit-speed-comparison/$1" [DPI,R=permanent]
RewriteRule "^inquiry(/.*)?$" "/bits$1" [DPI,R=permanent]
# Rewrite requests for php files to extensionless files, where absent
RewriteCond "%{REQUEST_FILENAME}" !-f
RewriteRule "^(.*)\.php$" "$1" [DPI,R]
# Use URL with trailing slash as canonical URL for index pages
RewriteRule "^(.*/)index$" "$1" [DPI,R]
# Ensure all access to kevinlocke.name is done over HTTPS without www.
# This is for canonicalization, SEO, and SSL (since SAN doesn't include www)
# https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/
# Note: Other subdomains not included, since some should not be canonicalized
# (e.g. ipv6.kevinlocke.name and ipv4.kevinlocke.name)
# First, handle absolute URLs in case rewriting has been done already
RewriteRule "^https?://(www\.)?kevinlocke.name(.*)$" "https://kevinlocke.name$2" [R=permanent,L]
RewriteRule "^(https?://.*)$" "$1" [L]
RewriteCond %{HTTP_HOST} =www.kevinlocke.name [NC]
RewriteRule ^/?(.*) "https://kevinlocke.name/$1" [R=permanent,L]
RewriteCond %{HTTP_HOST} =kevinlocke.name [NC]
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) "https://kevinlocke.name/$1" [R=permanent,L]
# Prevent access to .git directories
# Note: L implied by F, so put after HTTPS redirect
RewriteRule "^(.*/)?\.git/" - [F]
# Set a cache expiration on 301 responses to prevent indefinite caching.
# (Since if we screw up a redirect, there would be no way to fix it.)
# FIXME: Should set Expires, but can't find a way to conditionally set it
Header always set Cache-Control "max-age=86400" "expr=%{REQUEST_STATUS} == 301"
# Require HTTPS for the next 6 months on kevinlocke.name and subdomains
# Note: Has no effect when served without SSL, so don't bother.
Header always set Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" "expr=%{HTTPS} == 'on'"
# Define media types for negotiation
# We use .gz as an encoding, not a type.
RemoveType .gz
# .tar.gz is an exception and should be served as application/gzip
<FilesMatch ".+\.tar\.gz$">
RemoveEncoding .gz
# Note: Can use application/x-gzip for backwards-compatibility
AddType application/gzip .gz
# Alternatively:
#ForceType application/gzip
</FilesMatch>
# Prioritize XHTML slightly above HTML
<If "%{HTTP_ACCEPT} =~ m#application/xhtml\+xml#i">
AddType text/html;qs=0.99 .html
AddType application/xhtml+xml .xhtml
</If>
<Else>
AddType text/html .html
AddType application/xhtml+xml;qs=0.99 .xhtml
</Else>
# Remove qs parameter incorrectly sent by MultiViews due to
# https://bz.apache.org/bugzilla/show_bug.cgi?id=53595
Header always edit "Content-Type" ";\s*qs=[0-9]*(?:\.[0-9]+)?\s*(?=;|$)" ""
# Define character sets (for negotiation and user agent interpretation)
AddCharset utf-8 .html
AddCharset utf-8 .xhtml
AddCharset utf-8 .atom
# Define encodings (for negotiation of pre-compressed files)
# Note: Can't use .br extension, which conflicts with ISO-693 br language code
# Using .bro as currently in https://github.com/google/brotli/pull/163
AddEncoding br .bro
AddEncoding gzip .gz
# Set the default Content-Language to English (since most pages are)
DefaultLanguage en
# Although the DEFLATE filter will only compress when the request accepts gzip
# and there is the response is not already compressed, mod_filter applies the
# header changes (e.g. removing Etag, setting Accept-Ranges: none) whenever
# the provider matches. To avoid this, only match the provider when it has
# a chance of actually applying.
#
# Note: %{resp:Content-Encoding} is not set by mod_negotiation.c, which only
# sets the content_encoding field of the response struct. Check
# %{resp:Content-Location} which has the matched file extension, to handle it.
<IfVersion < 2.4.4>
# Note: Can't use / in FilterProvider regex in Apache < 2.2.21 use \x2F
# See https://issues.apache.org/bugzilla/show_bug.cgi?id=51434
# Gzip content which may benefit from the compression
FilterDeclare COMPRESS CONTENT_SET
# Compress any text content
FilterProvider COMPRESS DEFLATE resp=Content-Type /^text\x2F/
# Compress JavaScript
FilterProvider COMPRESS DEFLATE resp=Content-Type /^application\x2F(x-)?javascript/
# Compress any XML content
# Note: This expression could be a lot tighter, but this is simple and works
FilterProvider COMPRESS DEFLATE resp=Content-Type /^[^;]+(\x2F|\+)xml\x20*(;|$)/
FilterChain COMPRESS
FilterProtocol COMPRESS DEFLATE change=yes,byteranges=no
</IfVersion>
<IfVersion >= 2.4.4>
<If "%{req:Accept-Encoding} =~ m#\bgzip\b#i">
# Gzip content which may benefit from the compression
FilterDeclare COMPRESS CONTENT_SET
# Compress any text content
FilterProvider COMPRESS DEFLATE "%{Content_Type} =~ m#^text/#i && -z %{resp:Content-Encoding} && %{resp:Content-Location} !~ m/\.(?:bro?|gz)$/"
# Compress JavaScript
FilterProvider COMPRESS DEFLATE "%{Content_Type} =~ m#^application/(x-)?javascript#i && -z %{resp:Content-Encoding} && %{resp:Content-Location} !~ m/\.(?:bro?|gz)$/"
# Compress any XML content
# Note: This expression could be a lot tighter, but this is simple and works
FilterProvider COMPRESS DEFLATE "%{Content_Type} =~ m#^[^;]+(/|\+)xml *(;|$)#i && -z %{resp:Content-Encoding} && %{resp:Content-Location} !~ m/\.(?:bro?|gz)$/"
FilterChain COMPRESS
FilterProtocol COMPRESS DEFLATE change=yes,byteranges=no
</If>
</IfVersion>