mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +08:00
Previously, enabling `cache_control_bfcache_compatibility` (the experiment currently running on meta, #38763) made HTML documents storable in the browser HTTP cache, and browsers skip revalidation on history navigations — so back/forward, session restore, and discarded-tab reloads could resurrect a days-old document: stale topic lists that only get older, and logged-in UI shown to logged-out sessions (and vice versa). Reported in https://meta.discourse.org/t/400459. This change keeps the documents out of shared caches (`no-cache, private`), reloads any document that was served from the HTTP cache on a history navigation (navigation entry with `type === "back_forward"` and `transferSize === 0` — a forced reload gets type `"reload"`, so it cannot loop), and validates the session on `pageshow` restores from the back/forward cache, reloading when the logged-in user no longer matches the one the page booted with. Both client-side checks only run when the setting is enabled; the full rationale (browser-engine specifics, why `transferSize` rather than `deliveryType`, why `fetch` rather than `ajax`) is in the commit message. Reproduced and verified end-to-end in Chromium and Firefox: with the setting enabled, `goBack()` served `/latest` with zero network contact — stale list, wrong login state, and the exact `403 /u/:username/private-message-topic-tracking-state` errors from the meta report; with this change the same navigation heals with a single automatic reload, and a control run with the setting disabled behaves as before.
172 lines
5.9 KiB
Text
Vendored
172 lines
5.9 KiB
Text
Vendored
<!DOCTYPE html>
|
|
<html lang="<%= html_lang %>" class="<%= html_classes %>">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title><%= title_content %></title>
|
|
<meta name="description" content="<%= description_content %>">
|
|
<meta name="discourse_theme_id" content="<%= theme_id %>">
|
|
<meta name="discourse_current_homepage" content="<%= current_homepage %>">
|
|
|
|
<%- if GlobalSetting.early_hint_header_mode == "prefetch" %>
|
|
<%= render partial: "common/discourse_preload_stylesheet" %>
|
|
<%- end %>
|
|
<%= render partial: "layouts/head" %>
|
|
<%= discourse_csrf_tags %>
|
|
|
|
<%- if SiteSetting.enable_escaped_fragments? %>
|
|
<meta name="fragment" content="!">
|
|
<%- end %>
|
|
|
|
<%- if shared_session_key %>
|
|
<meta name="shared_session_key" content="<%= shared_session_key %>">
|
|
<%- end %>
|
|
|
|
<%= render("layouts/plugin_js", opts: {
|
|
include_official: allow_plugins?,
|
|
include_unofficial: allow_third_party_plugins?,
|
|
request: request,
|
|
include_admin_asset: staff?
|
|
})
|
|
%>
|
|
|
|
<%= build_plugin_html 'server:before-script-load' %>
|
|
|
|
<%- if bfcache_compatibility_mode? %>
|
|
<script id="bfcache-stale-document-check" nonce="<%= csp_nonce_placeholder %>">
|
|
{
|
|
const nav = performance.getEntriesByType("navigation")[0];
|
|
if (nav?.type === "back_forward" && nav.transferSize === 0) {
|
|
location.reload();
|
|
}
|
|
}
|
|
</script>
|
|
<%- end %>
|
|
|
|
<script nonce="<%= csp_nonce_placeholder %>">
|
|
window.EmberENV ??= {};
|
|
window.EmberENV._DEFAULT_ASYNC_OBSERVERS = true;
|
|
</script>
|
|
|
|
<%= preload_script_url ExtraLocalesController.url("main"), type_module: true %>
|
|
<%= preload_script_url ExtraLocalesController.url("mf"), type_module: true %>
|
|
<%- if ExtraLocalesController.client_overrides_exist? %>
|
|
<%= preload_script_url ExtraLocalesController.url("overrides"), type_module: true %>
|
|
<%- end %>
|
|
<%- if staff? %>
|
|
<%= preload_script_url ExtraLocalesController.url("admin"), type_module: true %>
|
|
<%- end %>
|
|
<%- if admin? %>
|
|
<%= preload_script_url ExtraLocalesController.url("wizard"), type_module: true %>
|
|
<%- end %>
|
|
<%- unless customization_disabled? %>
|
|
<%= theme_translations_lookup %>
|
|
<%- end %>
|
|
|
|
<link rel="preload" href="<%= script_asset_path "js/browser-update" %>" as="script" nonce="<%= csp_nonce_placeholder %>">
|
|
|
|
<%= preload_script 'js/browser-detect' %>
|
|
|
|
<%= preload_script "vendor", type_module: true %>
|
|
|
|
<%= module_preloads_for "discourse", (staff? ? "admin/compat-modules" : nil) %>
|
|
|
|
<%- unless customization_disabled? %>
|
|
<%- theme_js_assets.each do |asset| %>
|
|
<link rel="modulepreload" href="<%= asset[:url] %>" data-theme-id="<%= asset[:theme_id] %>" nonce="<%= csp_nonce_placeholder %>">
|
|
<%- end %>
|
|
<%= theme_lookup("head_tag") %>
|
|
<%- end %>
|
|
|
|
<%= render_google_tag_manager_head_code %>
|
|
<%= render_google_universal_analytics_code %>
|
|
<%= render_adobe_analytics_tags_code %>
|
|
|
|
<link id="manifest-link" rel="manifest" href=<%= manifest_url %> crossorigin="use-credentials">
|
|
|
|
<%- if include_ios_native_app_banner? %>
|
|
<meta name="apple-itunes-app" content="app-id=<%= SiteSetting.ios_app_id %><%= ios_app_argument %>">
|
|
<%- end %>
|
|
|
|
<%= yield :head %>
|
|
|
|
<%= build_plugin_html 'server:before-head-close' %>
|
|
|
|
<%= tag.meta id: 'data-discourse-setup', data: client_side_setup_data %>
|
|
|
|
<meta name="discourse/config/environment" content="<%=u discourse_config_environment %>" />
|
|
<%- if authentication_data %>
|
|
<meta id="data-authentication" data-authentication-data="<%= authentication_data %>">
|
|
<%- end %>
|
|
</head>
|
|
|
|
<body class="<%= body_classes %>">
|
|
<%- if include_splash_screen? %>
|
|
<%= render partial: "common/discourse_splash" %>
|
|
<%- end %>
|
|
|
|
<discourse-assets>
|
|
<discourse-assets-stylesheets>
|
|
<%= render partial: "common/discourse_stylesheet" %>
|
|
</discourse-assets-stylesheets>
|
|
<discourse-assets-json>
|
|
<script type="application/json" id="data-preloaded"><%= raw(preloaded_json) %></script>
|
|
</discourse-assets-json>
|
|
<discourse-assets-icons></discourse-assets-icons>
|
|
</discourse-assets>
|
|
|
|
<%- if allow_plugins? %>
|
|
<%= build_plugin_html 'server:after-body-open' %>
|
|
<%- end -%>
|
|
|
|
<%= render_google_tag_manager_body_code %>
|
|
<noscript data-path="<%= request.env['PATH_INFO'] %>">
|
|
<%= escape_noscript do %>
|
|
<%= render partial: "layouts/noscript_header" %>
|
|
|
|
<div id="main-outlet" class="wrap" role="main">
|
|
<!-- preload-content: -->
|
|
<%= yield %>
|
|
<!-- :preload-content -->
|
|
</div>
|
|
|
|
<%= render partial: "layouts/noscript_footer" %>
|
|
<% end %>
|
|
</noscript>
|
|
|
|
<%- unless customization_disabled? %>
|
|
<%= theme_lookup("header") %>
|
|
<%- end %>
|
|
|
|
<%- if allow_plugins? %>
|
|
<%= build_plugin_html 'server:header' %>
|
|
<%- end %>
|
|
|
|
<section id='main'>
|
|
</section>
|
|
|
|
<% unless current_user %>
|
|
<form id='hidden-login-form' method="post" action="<%=main_app.login_path%>" style="display: none;">
|
|
<input name="username" type="text" id="signin_username">
|
|
<input name="password" type="password" id="signin_password">
|
|
<input name="redirect" type="hidden">
|
|
<input type="submit" id="signin-button" value="<%= t 'log_in' %>">
|
|
</form>
|
|
<% end %>
|
|
|
|
<%= preload_script "discourse", type_module: true %>
|
|
|
|
<script defer src="<%= script_asset_path "js/browser-update" %>" nonce="<%= csp_nonce_placeholder %>"></script>
|
|
|
|
<%= yield :data %>
|
|
|
|
<%- unless customization_disabled? %>
|
|
<%= theme_lookup("body_tag") %>
|
|
<%- end %>
|
|
|
|
<%- if allow_plugins? %>
|
|
<%= build_plugin_html 'server:before-body-close' %>
|
|
<%- end %>
|
|
|
|
<script nonce="<%= csp_nonce_placeholder %>">/* Workaround for https://bugs.webkit.org/show_bug.cgi?id=209261 */</script>
|
|
</body>
|
|
</html>
|