diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json
index f20b71d959b..9ab4b2e7e14 100644
--- a/packages/frontend/@n8n/i18n/src/locales/en.json
+++ b/packages/frontend/@n8n/i18n/src/locales/en.json
@@ -3072,6 +3072,11 @@
"saveButton.saved": "Saved",
"saveButton.saving": "Saving",
"settings": "Settings",
+ "settings.authCookie.insecureConnection.title": "Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari.",
+ "settings.authCookie.insecureConnection.fixIntro": "To fix this, please consider the following options:",
+ "settings.authCookie.insecureConnection.option.tls": "Setup TLS/HTTPS (recommended), or",
+ "settings.authCookie.insecureConnection.option.localhost": "If you are running this locally, and not using Safari, try using localhost instead",
+ "settings.authCookie.insecureConnection.option.disableSecureCookie": "If you prefer to disable this security feature (not recommended), set the environment variable {envVar} to false",
"settings.communityNodes": "Community nodes",
"settings.communityNodes.empty.title": "Supercharge your workflows with community nodes",
"settings.communityNodes.empty.verified.only.title": "Supercharge your workflows with verified community nodes",
diff --git a/packages/frontend/@n8n/stores/package.json b/packages/frontend/@n8n/stores/package.json
index 45810e600c6..f6a7e967d29 100644
--- a/packages/frontend/@n8n/stores/package.json
+++ b/packages/frontend/@n8n/stores/package.json
@@ -37,6 +37,7 @@
},
"dependencies": {
"@n8n/api-types": "workspace:*",
+ "@n8n/i18n": "workspace:*",
"@n8n/permissions": "workspace:*",
"@n8n/rest-api-client": "workspace:*",
"n8n-workflow": "workspace:*",
diff --git a/packages/frontend/@n8n/stores/src/settings.store.test.ts b/packages/frontend/@n8n/stores/src/settings.store.test.ts
index 4619fb97784..4412ff9a994 100644
--- a/packages/frontend/@n8n/stores/src/settings.store.test.ts
+++ b/packages/frontend/@n8n/stores/src/settings.store.test.ts
@@ -1,5 +1,7 @@
import type { FrontendSettings } from '@n8n/api-types';
+import { i18n } from '@n8n/i18n';
import { createPinia, setActivePinia } from 'pinia';
+import type { MockInstance } from 'vitest';
import { mock } from 'vitest-mock-extended';
import { useSettingsStore } from './settings.store';
@@ -167,6 +169,56 @@ describe('settings.store', () => {
});
});
+ describe('insecure connection warning', () => {
+ let writeSpy: MockInstance<(...text: string[]) => void>;
+
+ beforeEach(() => {
+ writeSpy = vi.spyOn(document, 'write').mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ vi.unstubAllGlobals();
+ });
+
+ it('should render the localized warning over an insecure, non-localhost origin', async () => {
+ vi.stubGlobal('location', { protocol: 'http:', hostname: 'n8n.example.com' });
+
+ getSettings.mockResolvedValueOnce({ ...mockSettings, authCookie: { secure: true } });
+
+ await useSettingsStore().getSettings();
+
+ expect(writeSpy).toHaveBeenCalledTimes(1);
+ const markup = writeSpy.mock.calls[0][0];
+ expect(markup).toContain(i18n.baseText('settings.authCookie.insecureConnection.title'));
+ expect(markup).toContain('N8N_SECURE_COOKIE');
+ expect(markup).toContain('http://localhost:5678');
+ });
+
+ it('should not render the warning over localhost in a non-Safari browser', async () => {
+ vi.stubGlobal('location', { protocol: 'http:', hostname: 'localhost' });
+ vi.stubGlobal('navigator', {
+ userAgent:
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0',
+ });
+
+ getSettings.mockResolvedValueOnce({ ...mockSettings, authCookie: { secure: true } });
+
+ await useSettingsStore().getSettings();
+
+ expect(writeSpy).not.toHaveBeenCalled();
+ });
+
+ it('should not render the warning when the cookie is not secure', async () => {
+ vi.stubGlobal('location', { protocol: 'http:', hostname: 'n8n.example.com' });
+
+ getSettings.mockResolvedValueOnce({ ...mockSettings, authCookie: { secure: false } });
+
+ await useSettingsStore().getSettings();
+
+ expect(writeSpy).not.toHaveBeenCalled();
+ });
+ });
+
describe('getSettings', () => {
describe('telemetry', () => {
it('should fetch settings and call sessionStarted if telemetry is enabled', async () => {
diff --git a/packages/frontend/@n8n/stores/src/settings.store.ts b/packages/frontend/@n8n/stores/src/settings.store.ts
index 9bc2d6d60e1..cbc251a03ce 100644
--- a/packages/frontend/@n8n/stores/src/settings.store.ts
+++ b/packages/frontend/@n8n/stores/src/settings.store.ts
@@ -5,6 +5,7 @@ import {
type FrontendModuleSettings,
type WorkflowReviewsPolicy,
} from '@n8n/api-types';
+import { i18n } from '@n8n/i18n';
import { makeRestApiRequest } from '@n8n/rest-api-client';
import * as aiUsageApi from '@n8n/rest-api-client/api/ai-usage';
import * as eventsApi from '@n8n/rest-api-client/api/events';
@@ -22,19 +23,20 @@ import { useRootStore } from './useRootStore';
/**
* Full-page warning rendered when the instance requires a secure cookie but the
* page is served over an insecure origin (or via Safari, which drops the cookie).
+ * The copy is localized; the structural markup and inline styles stay in code
+ * because this is written via `document.write` before the Vue app mounts.
*/
-const INSECURE_CONNECTION_WARNING = `
+const buildInsecureConnectionWarning = () => `
N8N_SECURE_COOKIE to false