diff --git a/CHANGES.rst b/CHANGES.rst
index 664e69b51ac46c2f01fce5867ebfca9bb794b79b..a42ec92f9a53149ce26332bff518883f23e15946 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -9,6 +9,11 @@ Changes
 =======
 
 
+Version 2025.1.8 (released 2025-02-13)
+
+- Explicitly set calculated value for `THEME_SITEURL` in the config
+
+
 Version 2025.1.7 (released 2025-02-11)
 
 - Override `BROKER_URL` more aggressively
diff --git a/invenio_config_tuw/__init__.py b/invenio_config_tuw/__init__.py
index a888db7d8be5e270a1ec632221cad4b3301d9d4c..a902ac3ac244dab1aec9fd1eca6a89960d726f71 100644
--- a/invenio_config_tuw/__init__.py
+++ b/invenio_config_tuw/__init__.py
@@ -9,6 +9,6 @@
 
 from .ext import InvenioConfigTUW
 
-__version__ = "2025.1.7"
+__version__ = "2025.1.8"
 
 __all__ = ("__version__", "InvenioConfigTUW")
diff --git a/invenio_config_tuw/startup/config.py b/invenio_config_tuw/startup/config.py
index 84271b9f85278628c61a0bf776c50aac8e2b3368..2ed303aefc47e839d8342f929ef0887b647bb69c 100644
--- a/invenio_config_tuw/startup/config.py
+++ b/invenio_config_tuw/startup/config.py
@@ -177,20 +177,30 @@ def assemble_cache_uri_from_parts(app):
 
 def assemble_site_urls_from_parts(app):
     """Create `LocalProxy` objects for the `SITE_{API,UI}_URL` items."""
-    # prefer the `SERVER_NAME` config item to build URLs
     server_name = _get_config(app, "SERVER_NAME")
-    theme_siteurl = _get_config(
-        app, "THEME_SITEURL", default=f"https://{server_name or 'localhost'}"
-    )
+    preferred_scheme = _get_config(app, "PREFERRED_URL_SCHEME", "https")
+    theme_siteurl = _get_config(app, "THEME_SITEURL")
+
+    # note: the preferred way is setting the `SERVER_NAME` configuration
     if server_name:
-        hostname = server_name
+        theme_siteurl = theme_siteurl or f"{preferred_scheme}://{server_name}"
+
+    elif theme_siteurl:
+        server_name = theme_siteurl.lstrip("http://").lstrip("https://").split("/")[0]
+        app.logger.info(
+            f"No SERVER_NAME set, calculated value '{server_name}' from THEME_SITEURL: '{theme_siteurl}'"
+        )
+
     else:
-        hostname = theme_siteurl.lstrip("http://").lstrip("https://").split("/")[0]
+        raise RuntimeError(
+            "Neither SERVER_NAME or THEME_SITEURL are configured. Aborting."
+        )
 
     # note: 'invenio-cli run' likes to populate INVENIO_SITE_{UI,API}_URL...
     app.config["SITE_UI_URL"] = LocalProxy(partial(_make_site_url, ""))
     app.config["SITE_API_URL"] = LocalProxy(partial(_make_site_url, "/api"))
-    app.config["OAISERVER_ID_PREFIX"] = hostname
+    app.config["THEME_SITEURL"] = theme_siteurl
+    app.config["OAISERVER_ID_PREFIX"] = server_name
 
 
 def assemble_keycloak_config_from_parts(app):
diff --git a/tests/conftest.py b/tests/conftest.py
index 8fb7f7f50b99c1af5bf736f8dad07bb86677ef6b..331b76e87f32d0012ff51dc4ceee3efa2d2ac61d 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -120,6 +120,7 @@ def app_config(app_config):
     app_config["WTF_CSRF_ENABLED"] = False
     app_config["MAIL_SUPPRESS_SEND"] = True
     app_config["WEBPACKEXT_MANIFEST_LOADER"] = MockManifestLoader
+    app_config["SERVER_NAME"] = "localhost"
 
     return app_config
 
diff --git a/tests/test_invenio_config_tuw.py b/tests/test_invenio_config_tuw.py
index a72a527f94d215ca9389526df83f671feeb284c4..687441da27e1c4c47e666f01ee538bae42613e68 100644
--- a/tests/test_invenio_config_tuw.py
+++ b/tests/test_invenio_config_tuw.py
@@ -22,10 +22,12 @@ def test_version():
 def test_init():
     """Test extension initialization."""
     app = Flask("testapp")
+    app.config["SERVER_NAME"] = "localhost"
     InvenioConfigTUW(app)
     assert "invenio-config-tuw" in app.extensions
 
     app = Flask("testapp")
+    app.config["SERVER_NAME"] = "localhost"
     ext = InvenioConfigTUW()
     assert "invenio-config-tuw" not in app.extensions
     ext.init_app(app)