fix: drop Clear-Site-Data reset breaking mini app styles

The once-per-version Clear-Site-Data: "cache" header on the index
navigation raced the page's own CSS/JS subresource loads in the
Telegram WebView, intermittently evicting or aborting the main
stylesheet so the mini app rendered half-styled on mobile.

It also could not fix stale HTML: it only fires when the document
actually reaches the backend, never when the WebView serves a cached
page. The no-store HTML plus immutable content-hashed asset filenames
already guarantee freshness without clearing the cache, so remove the
reset header, its helpers, constants, and tests.
This commit is contained in:
3252a8
2026-06-08 22:12:16 +03:00
parent c2f0ae0b8b
commit 0db3a68c09
2 changed files with 0 additions and 83 deletions
-37
View File
@@ -20,9 +20,6 @@ _I18N_PAYLOAD_CACHE: Dict[tuple[int, str, tuple[tuple[str, int, int], ...]], Dic
_ASSET_NAME_CACHE_TTL_SECONDS = 30.0
WEBAPP_HTML_CACHE_CONTROL = "no-store, no-cache, must-revalidate, max-age=0"
WEBAPP_LEGACY_ASSET_CACHE_CONTROL = "no-store, no-cache, must-revalidate, max-age=0"
WEBAPP_CACHE_RESET_COOKIE_NAME = "rw_webapp_cache_v"
WEBAPP_CACHE_RESET_COOKIE_MAX_AGE_SECONDS = 365 * 24 * 60 * 60
WEBAPP_CACHE_RESET_DIGEST_LENGTH = 16
async def health_route(request: web.Request) -> web.Response:
@@ -1132,35 +1129,6 @@ def _apply_webapp_head_metadata(html_text: str, page_title: str, favicon_url: st
return _replace_webapp_favicon(html_text, _favicon_head_markup(favicon_url))
def _webapp_cache_reset_version(css_asset_name: str, js_asset_name: str) -> str:
version = str(_resolve_app_version() or "dev+unknown").strip() or "dev+unknown"
raw = f"{version}|{css_asset_name}|{js_asset_name}"
digest = hashlib.sha256(raw.encode("utf-8")).hexdigest()
return digest[:WEBAPP_CACHE_RESET_DIGEST_LENGTH]
def _apply_webapp_cache_reset_header(
request: web.Request,
response: web.StreamResponse,
cache_version: str,
) -> None:
"""Ask capable WebViews to clear stale HTTP cache once per deployed asset set."""
if not cache_version:
return
if request.cookies.get(WEBAPP_CACHE_RESET_COOKIE_NAME) == cache_version:
return
response.headers["Clear-Site-Data"] = '"cache"'
response.set_cookie(
WEBAPP_CACHE_RESET_COOKIE_NAME,
cache_version,
httponly=True,
secure=True,
samesite="None",
path="/",
max_age=WEBAPP_CACHE_RESET_COOKIE_MAX_AGE_SECONDS,
)
async def index_route(request: web.Request) -> web.Response:
settings: Settings = request.app["settings"]
if not settings.WEBAPP_ENABLED:
@@ -1221,11 +1189,6 @@ async def index_route(request: web.Request) -> web.Response:
response.headers["Cache-Control"] = WEBAPP_HTML_CACHE_CONTROL
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
_apply_webapp_cache_reset_header(
request,
response,
_webapp_cache_reset_version(css_asset_name, js_asset_name),
)
return response
-46
View File
@@ -523,52 +523,6 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
self.assertEqual(exc.exception.location, "/uploaded-icon.png")
self.assertEqual(exc.exception.headers["Cache-Control"], "no-cache")
def test_webapp_cache_reset_version_changes_with_asset_set(self):
with patch.object(webapp_assets, "_resolve_app_version", return_value="v1.2.3"):
first = webapp_assets._webapp_cache_reset_version(
"subscription_webapp.11111111.css",
"subscription_webapp.min.22222222.js",
)
second = webapp_assets._webapp_cache_reset_version(
"subscription_webapp.33333333.css",
"subscription_webapp.min.22222222.js",
)
self.assertRegex(first, r"^[0-9a-f]{16}$")
self.assertNotEqual(first, second)
def test_webapp_cache_reset_header_is_sent_once_per_version(self):
request = SimpleNamespace(cookies={})
response = web.Response()
webapp_assets._apply_webapp_cache_reset_header(request, response, "abc123")
self.assertEqual(response.headers["Clear-Site-Data"], '"cache"')
cookie = response.cookies[webapp_assets.WEBAPP_CACHE_RESET_COOKIE_NAME]
self.assertEqual(cookie.value, "abc123")
self.assertEqual(cookie["path"], "/")
self.assertEqual(
cookie["max-age"],
str(webapp_assets.WEBAPP_CACHE_RESET_COOKIE_MAX_AGE_SECONDS),
)
self.assertTrue(cookie["httponly"])
self.assertTrue(cookie["secure"])
self.assertEqual(cookie["samesite"], "None")
cached_request = SimpleNamespace(
cookies={webapp_assets.WEBAPP_CACHE_RESET_COOKIE_NAME: "abc123"}
)
cached_response = web.Response()
webapp_assets._apply_webapp_cache_reset_header(
cached_request,
cached_response,
"abc123",
)
self.assertNotIn("Clear-Site-Data", cached_response.headers)
self.assertNotIn(webapp_assets.WEBAPP_CACHE_RESET_COOKIE_NAME, cached_response.cookies)
async def test_default_logo_route_serves_bundled_logo(self):
settings = SimpleNamespace(WEBAPP_ENABLED=True)
request = SimpleNamespace(app={"settings": settings})