Skip to content

Commit c5cbc65

Browse files
committed
Add charset=utf-8 to Content-Type for CSS and HTML assets
CSS and HTML assets served by Propshaft::Server now include "; charset=utf-8" in their Content-Type response header. This fixes browser encoding issues where non-ASCII characters in CSS files (e.g., en-dash in content properties) render as mojibake when Chrome falls back to Windows-1252 encoding.
1 parent 68bb8c5 commit c5cbc65

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

lib/propshaft/server.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ def call(env)
1919
if (asset = @assembly.load_path.find(path)) && asset.fresh?(digest)
2020
compiled_content = asset.compiled_content
2121

22+
content_type = asset.content_type.to_s
23+
if DEFAULT_UTF8_CONTENT_TYPES.include?(content_type)
24+
content_type = "#{content_type}; charset=utf-8"
25+
end
26+
2227
[
2328
200,
2429
{
2530
Rack::CONTENT_LENGTH => compiled_content.length.to_s,
26-
Rack::CONTENT_TYPE => asset.content_type.to_s,
31+
Rack::CONTENT_TYPE => content_type,
2732
VARY => "Accept-Encoding",
2833
Rack::ETAG => "\"#{asset.digest}\"",
2934
Rack::CACHE_CONTROL => "public, max-age=31536000, immutable"
@@ -43,6 +48,12 @@ def inspect
4348
end
4449

4550
private
51+
DEFAULT_UTF8_CONTENT_TYPES = [
52+
Rack::Mime::MIME_TYPES[".css"],
53+
Rack::Mime::MIME_TYPES[".html"],
54+
]
55+
private_constant :DEFAULT_UTF8_CONTENT_TYPES
56+
4657
def extract_path_and_digest(path)
4758
path = path.delete_prefix(@assembly.prefix)
4859
path = Rack::Utils.unescape(path)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<html><body>hello</body></html>

test/propshaft/server_test.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def call(env)
4747

4848
assert_equal 200, last_response.status
4949
assert_equal last_response.body.bytesize.to_s, last_response.headers['content-length']
50-
assert_equal "text/css", last_response.headers['content-type']
50+
assert_equal "text/css; charset=utf-8", last_response.headers['content-type']
5151
assert_equal "Accept-Encoding", last_response.headers['vary']
5252
assert_equal "\"#{asset.digest}\"", last_response.headers['etag']
5353
assert_equal "public, max-age=31536000, immutable", last_response.headers['cache-control']
@@ -67,6 +67,22 @@ def call(env)
6767
assert_equal 200, last_response.status
6868
end
6969

70+
test "serve an HTML file with charset" do
71+
asset = @assembly.load_path.find("test.html")
72+
get "/assets/#{asset.digested_path}"
73+
74+
assert_equal 200, last_response.status
75+
assert_equal "text/html; charset=utf-8", last_response.headers['content-type']
76+
end
77+
78+
test "serve a JS file without charset" do
79+
asset = @assembly.load_path.find("again.js")
80+
get "/assets/#{asset.digested_path}"
81+
82+
assert_equal 200, last_response.status
83+
assert_equal "text/javascript", last_response.headers['content-type']
84+
end
85+
7086
test "not found" do
7187
get "/assets/not-found.js"
7288

0 commit comments

Comments
 (0)