diff --git a/app/assets/javascripts/discourse/app/lib/theme-settings-store.js b/app/assets/javascripts/discourse/app/lib/theme-settings-store.js
index 6503d6b932c..940a88cf4c2 100644
--- a/app/assets/javascripts/discourse/app/lib/theme-settings-store.js
+++ b/app/assets/javascripts/discourse/app/lib/theme-settings-store.js
@@ -1,4 +1,5 @@
 import { get } from "@ember/object";
+import { cloneJSON } from "discourse-common/lib/object";
 
 const originalSettings = {};
 const settings = {};
@@ -11,7 +12,7 @@ export function registerSettings(
   if (settings[themeId] && !force) {
     return;
   }
-  originalSettings[themeId] = Object.assign({}, settingsObject);
+  originalSettings[themeId] = cloneJSON(settingsObject);
   const s = {};
   Object.keys(settingsObject).forEach((key) => {
     Object.defineProperty(s, key, {
@@ -41,7 +42,14 @@ export function getObjectForTheme(themeId) {
 export function resetSettings() {
   Object.keys(originalSettings).forEach((themeId) => {
     Object.keys(originalSettings[themeId]).forEach((key) => {
-      settings[themeId][key] = originalSettings[themeId][key];
+      const original = originalSettings[themeId][key];
+      if (original && typeof original === "object") {
+        // special handling for the theme_uploads and theme_uploads_local magic
+        // objects in settings
+        settings[themeId][key] = cloneJSON(original);
+      } else {
+        settings[themeId][key] = original;
+      }
     });
   });
 }
diff --git a/app/models/theme.rb b/app/models/theme.rb
index adce6dbabdb..6dbe460a22c 100644
--- a/app/models/theme.rb
+++ b/app/models/theme.rb
@@ -530,6 +530,13 @@ class Theme < ActiveRecord::Base
       self.settings.each do |setting|
         settings_hash[setting.name] = setting.default
       end
+
+      theme_uploads = build_theme_uploads_hash
+      settings_hash['theme_uploads'] = theme_uploads if theme_uploads.present?
+
+      theme_uploads_local = build_local_theme_uploads_hash
+      settings_hash['theme_uploads_local'] = theme_uploads_local if theme_uploads_local.present?
+
       settings_hash
     end
   end
@@ -540,24 +547,35 @@ class Theme < ActiveRecord::Base
       hash[setting.name] = setting.value
     end
 
-    theme_uploads = {}
-    theme_uploads_local = {}
-
-    upload_fields.each do |field|
-      if field.upload&.url
-        theme_uploads[field.name] = Discourse.store.cdn_url(field.upload.url)
-      end
-      if field.javascript_cache
-        theme_uploads_local[field.name] = field.javascript_cache.local_url
-      end
-    end
-
+    theme_uploads = build_theme_uploads_hash
     hash['theme_uploads'] = theme_uploads if theme_uploads.present?
+
+    theme_uploads_local = build_local_theme_uploads_hash
     hash['theme_uploads_local'] = theme_uploads_local if theme_uploads_local.present?
 
     hash
   end
 
+  def build_theme_uploads_hash
+    hash = {}
+    upload_fields.each do |field|
+      if field.upload&.url
+        hash[field.name] = Discourse.store.cdn_url(field.upload.url)
+      end
+    end
+    hash
+  end
+
+  def build_local_theme_uploads_hash
+    hash = {}
+    upload_fields.each do |field|
+      if field.javascript_cache
+        hash[field.name] = field.javascript_cache.local_url
+      end
+    end
+    hash
+  end
+
   def update_setting(setting_name, new_value)
     target_setting = settings.find { |setting| setting.name == setting_name }
     raise Discourse::NotFound unless target_setting
diff --git a/spec/requests/theme_javascripts_controller_spec.rb b/spec/requests/theme_javascripts_controller_spec.rb
index 0f2c58ce584..84ec854ae19 100644
--- a/spec/requests/theme_javascripts_controller_spec.rb
+++ b/spec/requests/theme_javascripts_controller_spec.rb
@@ -126,6 +126,34 @@ RSpec.describe ThemeJavascriptsController do
       expect(response.body).to include("assert.ok(true);")
     end
 
+    it "includes theme uploads URLs in the settings object" do
+      SiteSetting.authorized_extensions = "*"
+      js_file = Tempfile.new(["vendorlib", ".js"])
+      js_file.write("console.log(123);\n")
+      js_file.rewind
+      js_upload = UploadCreator.new(js_file, "vendorlib.js").create_for(Discourse::SYSTEM_USER_ID)
+      component.set_field(
+        type: :theme_upload_var,
+        target: :common,
+        name: "vendorlib",
+        upload_id: js_upload.id
+      )
+      component.save!
+      _, digest = component.baked_js_tests_with_digest
+
+      get "/theme-javascripts/tests/#{component.id}-#{digest}.js"
+      expect(response.body).to include(
+        "require(\"discourse/lib/theme-settings-store\").registerSettings(" +
+        "#{component.id}, {\"num_setting\":5,\"theme_uploads\":{\"vendorlib\":" +
+        "\"/uploads/default/test_#{ENV['TEST_ENV_NUMBER']}/original/1X/#{js_upload.sha1}.js\"},\"theme_uploads_local\":{\"vendorlib\":" +
+        "\"/theme-javascripts/#{js_upload.sha1}.js?__ws=test.localhost\"}}, { force: true });"
+      )
+      expect(response.body).to include("assert.ok(true);")
+    ensure
+      js_file&.close
+      js_file&.unlink
+    end
+
     it "responds with 404 if digest is not a 40 chars hex" do
       digest = Rack::Utils.escape('../../../../../../../../../../etc/passwd').gsub('.', '%2E')
       get "/theme-javascripts/tests/#{component.id}-#{digest}.js"