diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 728aa48e283..ad37850f046 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -53,7 +53,7 @@ class ApplicationController < ActionController::Base after_action :add_noindex_header_to_non_canonical, if: :spa_boot_request? after_action :set_cross_origin_opener_policy_header, if: :spa_boot_request? after_action :clean_xml, if: :is_feed_response? - around_action :link_preload, if: -> { spa_boot_request? && GlobalSetting.preload_link_header } + around_action :add_link_header, if: -> { spa_boot_request? } HONEYPOT_KEY ||= "HONEYPOT_KEY" CHALLENGE_KEY ||= "CHALLENGE_KEY" @@ -1096,10 +1096,29 @@ class ApplicationController < ActionController::Base result end - def link_preload - @links_to_preload = [] + def add_link_header + @links_to_preload = [] if GlobalSetting.preload_link_header + yield - response.headers["Link"] = @links_to_preload.join(", ") if !@links_to_preload.empty? + + links = [] + + if SiteSetting.experimental_preconnect_link_header + [GlobalSetting.cdn_url, SiteSetting.s3_cdn_url].each do |url| + next if url.blank? + base_url = URI.join(url, "/").to_s.chomp("/") + + links.push("<#{base_url}>; rel=preconnect;") + # Not all browsers support the preconnect resource hint so we are adding dns-prefetch as the fallback + links.push("<#{base_url}>; rel=dns-prefetch;") + end + end + + if GlobalSetting.preload_link_header && !@links_to_preload.empty? + links = links.concat(@links_to_preload) + end + + response.headers["Link"] = links.join(", ") if links.present? end def spa_boot_request? diff --git a/config/discourse_defaults.conf b/config/discourse_defaults.conf index 7e4d723f881..04db7f940dc 100644 --- a/config/discourse_defaults.conf +++ b/config/discourse_defaults.conf @@ -390,7 +390,7 @@ log_line_max_chars = 160000 # this value is included when generating static asset URLs. # Updating the value will allow site operators to invalidate all asset urls # to recover from configuration issues which may have been cached by CDNs/browsers. -asset_url_salt = +asset_url_salt = # disable service-worker caching. An 'offline' page will still be used as an offline fallback. # This flag is for a temporary experiment, and may be removed at any time diff --git a/config/site_settings.yml b/config/site_settings.yml index 5555c155a8e..225a32e96de 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2368,6 +2368,9 @@ developer: experimental_objects_type_for_theme_settings: default: false hidden: true + experimental_preconnect_link_header: + default: false + hidden: true navigation: navigation_menu: diff --git a/spec/requests/application_controller_spec.rb b/spec/requests/application_controller_spec.rb index 181dad998a0..a280710c75f 100644 --- a/spec/requests/application_controller_spec.rb +++ b/spec/requests/application_controller_spec.rb @@ -1188,8 +1188,36 @@ RSpec.describe ApplicationController do end end - describe "preload Link header" do - context "with GlobalSetting.preload_link_header" do + describe "Link header" do + describe "when `experimental_preconnect_link_header` site setting is enabled" do + before { SiteSetting.experimental_preconnect_link_header = true } + + it "should include the `preconnect` and `dns-prefetch` resource hints in the Link header when `GlobalSetting.cdn_url is configured`" do + global_setting :cdn_url, "https://cdn.example.com/something" + + get "/latest" + + expect(response.status).to eq(200) + + expect(response.headers["Link"]).to include( + "; rel=preconnect;, ; rel=dns-prefetch;", + ) + end + + it "should include the `preconnect` and `dns-prefetch` resource hints in the Link header when `SiteSetting.s3_cdn_url is configured`" do + SiteSetting.s3_cdn_url = "https://s3.some-cdn.com/something" + + get "/latest" + + expect(response.status).to eq(200) + + expect(response.headers["Link"]).to include( + "; rel=preconnect;, ; rel=dns-prefetch;", + ) + end + end + + context "when `GlobalSetting.preload_link_header` is enabled" do before { global_setting :preload_link_header, true } it "should have the Link header with assets on full page requests" do @@ -1203,7 +1231,7 @@ RSpec.describe ApplicationController do end end - context "without GlobalSetting.preload_link_header" do + context "when `GlobalSetting.preload_link_header` is disabled" do before { global_setting :preload_link_header, false } it "shouldn't have the Link header with assets on full page requests" do