feat: send X-Powered-By: bulwarkmail.org on every response

This commit is contained in:
Linus Rath
2026-11-01 00:21:32 +01:00
parent eb2e3b2baa
commit a03f76ec85
2 changed files with 43 additions and 1 deletions
@@ -0,0 +1,35 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import type { NextConfig } from 'next';
import { getPathMatch } from 'next/dist/shared/lib/router/utils/path-match';
async function loadConfig(): Promise<NextConfig> {
vi.resetModules();
const { default: config } = await import('../../next.config');
return config;
}
afterEach(() => {
vi.resetModules();
});
describe('X-Powered-By header', () => {
it('names Bulwark instead of Next.js, without a version', async () => {
const config = await loadConfig();
const rules = (await config.headers?.()) ?? [];
const poweredBy = rules.flatMap((rule) =>
rule.headers
.filter((header) => header.key.toLowerCase() === 'x-powered-by')
.map((header) => ({ source: rule.source, value: header.value }))
);
expect(config.poweredByHeader).toBe(false);
expect(poweredBy).toEqual([{ source: '/:path*', value: 'bulwarkmail.org' }]);
});
it('matches the root, pages, API routes and static files', () => {
const match = getPathMatch('/:path*');
for (const path of ['/', '/en/mail', '/api/config', '/_next/static/chunks/main.js']) {
expect(match(path), path).not.toBe(false);
}
});
});
+8 -1
View File
@@ -49,7 +49,8 @@ if (isLite && (process.env.NEXT_PUBLIC_LOCALE_PREFIX ?? "never") !== "always") {
const nextConfig: NextConfig = {
output: isLite ? "export" : "standalone",
// No `X-Powered-By: Next.js` on every response.
// No `X-Powered-By: Next.js`; headers() below sends
// `X-Powered-By: bulwarkmail.org` instead, without a version.
poweredByHeader: false,
// Directory-style pages (out/en/mail/index.html) are what static hosts and
// the shipped SPA-fallback rules expect; next/image has no optimizer here.
@@ -77,6 +78,12 @@ const nextConfig: NextConfig = {
: {
async headers() {
return [
{
// Lets people see a site runs Bulwark. No version, so it tells
// a scanner nothing about which fixes are missing.
source: "/:path*",
headers: [{ key: "X-Powered-By", value: "bulwarkmail.org" }],
},
{
// Untrusted plugin iframes have opaque origins and load these public,
// immutable chunks in CORS mode. Next prefixes this source with basePath.