Skip to content

Commit 91cfb54

Browse files
otthedentarg
andauthored
Add :static_headers setting for custom headers in static file responses (#2089)
This PR implements recent feature request related to static file responses. It introduces a new configuration setting, `:static_headers`, which allows developers to define custom headers that will be applied to all static file responses served by `static!` -method. Sinatra serves static files directly via `static!`, bypassing filters and middleware. This makes it so that there is no good ways to add headers like `Access-Control-Allow-Origin`, which are often needed for CORS access (e.g., when using fonts or images on canvas). Co-authored-by: Patrik Ragnarsson <[email protected]>
1 parent c918134 commit 91cfb54

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,15 @@ Note that the public directory name is not included in the URL. A file
420420
Use the `:static_cache_control` setting (see [below](#cache-control)) to add
421421
`Cache-Control` header info.
422422

423+
By default, Sinatra serves static files from the `public/` folder without running middleware or filters. To add custom headers (e.g, for CORS or caching), use the `:static_headers` setting:
424+
425+
```ruby
426+
set :static_headers, {
427+
'access-control-allow-origin' => '*',
428+
'x-static-asset' => 'served-by-sinatra'
429+
}
430+
```
431+
423432
## Views / Templates
424433

425434
Each template language is exposed via its own rendering method. These
@@ -2157,6 +2166,16 @@ set :protection, :session => true
21572166
<tt>set :static_cache_control, [:public, :max_age => 300]</tt>
21582167
</dd>
21592168

2169+
<dt>static_headers</dt>
2170+
<dd>
2171+
Allows you to define custom header settings for static file responses.
2172+
</dd>
2173+
<dd>
2174+
For example: <br>
2175+
<tt>set :static_headers, {'access-control-allow-origin' => '*', 'x-static-asset' => 'served-by-sinatra'}</tt>
2176+
</dd>
2177+
2178+
21602179
<dt>threaded</dt>
21612180
<dd>
21622181
If set to <tt>true</tt>, will tell server to use

lib/sinatra/base.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ def route_missing
11431143

11441144
# Attempt to serve static files from public directory. Throws :halt when
11451145
# a matching file is found, returns nil otherwise.
1146+
# If custom static headers are defined, use them.
11461147
def static!(options = {})
11471148
return if (public_dir = settings.public_folder).nil?
11481149

@@ -1156,6 +1157,9 @@ def static!(options = {})
11561157

11571158
env['sinatra.static_file'] = path
11581159
cache_control(*settings.static_cache_control) if settings.static_cache_control?
1160+
1161+
headers(settings.static_headers) if settings.static_headers?
1162+
11591163
send_file path, options.merge(disposition: nil)
11601164
end
11611165

@@ -2011,6 +2015,8 @@ class << self
20112015
set :public_folder, proc { root && File.join(root, 'public') }
20122016
set :static, proc { public_folder && File.exist?(public_folder) }
20132017
set :static_cache_control, false
2018+
2019+
set :static_headers, {}
20142020

20152021
error ::Exception do
20162022
response.status = 500

test/static_test.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,35 @@ def assert_valid_range(http_range, range, path, file)
273273
assert response.headers.include?('Last-Modified')
274274
end
275275

276+
it 'applies custom headers defined in static_headers setting' do
277+
mock_app do
278+
set :static, true
279+
set :public_folder, __dir__
280+
set :static_headers, {
281+
'access-control-allow-origin' => '*',
282+
'x-static-test' => 'yes'
283+
}
284+
end
285+
286+
get "/#{File.basename(__FILE__)}"
287+
assert ok?
288+
assert_equal '*', response['access-control-allow-origin']
289+
assert_equal 'yes', response['x-static-test']
290+
end
291+
292+
it 'respects both static_cache_control and static_headers settings together' do
293+
mock_app do
294+
set :static, true
295+
set :public_folder, __dir__
296+
set :static_cache_control, [:public, max_age: 3600]
297+
set :static_headers, { 'x-static-test' => 'yes' }
298+
end
299+
300+
get "/#{File.basename(__FILE__)}"
301+
assert ok?
302+
assert_equal 'yes', response['x-static-test']
303+
assert_includes response['cache-control'], 'public'
304+
assert_match(/max-age=3600/, response['cache-control'])
305+
end
306+
276307
end

0 commit comments

Comments
 (0)