d17f75c423
- src/middleware.ts now sets HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, and Permissions-Policy on every response -- all were absent per the 2026-09-15 audit. CSP deliberately left out: the site uses unnoned inline scripts, inline style attributes throughout, and a runtime-configured analytics script host, so a real CSP needs its own pass with live testing rather than a guess. - SiteHeader.astro and SiteFooter.astro logo alt/aria-label said 'MNQ Catering y Eventos' (plural) while every title tag, the JSON-LD name, og:site_name, and package.json all say 'MNQ Catering y Evento' (singular) -- unified to the singular form used everywhere else.
47 lines
2.3 KiB
TypeScript
47 lines
2.3 KiB
TypeScript
import { defineMiddleware } from 'astro:middleware';
|
|
|
|
const CANONICAL_HOST = 'www.mnqeventos.es';
|
|
|
|
/**
|
|
* SEO: enforce the canonical host at the application layer. www.mnqeventos.es
|
|
* is the real, registered, live production domain. mnqcatering.com was an
|
|
* intended rename that was never actually registered (confirmed via WHOIS —
|
|
* no match) — do NOT set CANONICAL_HOST back to it unless that domain is
|
|
* actually purchased and its DNS points at this server, or this redirect
|
|
* will send every visitor to a host that doesn't resolve.
|
|
*
|
|
* NOTE: this must also be verified at the reverse-proxy/hosting layer — host
|
|
* detection here depends on how the Host header (or X-Forwarded-Host, when
|
|
* behind a proxy) actually arrives in production. If the proxy doesn't
|
|
* forward the original host, this check will see the wrong value.
|
|
*/
|
|
export const onRequest = defineMiddleware(async (context, next) => {
|
|
const requestHost = context.url.host;
|
|
|
|
if (requestHost !== CANONICAL_HOST) {
|
|
const redirectUrl = new URL(context.url.pathname + context.url.search, `https://${CANONICAL_HOST}`);
|
|
return context.redirect(redirectUrl.toString(), 301);
|
|
}
|
|
|
|
const response = await next();
|
|
|
|
// Baseline security headers (flagged missing in the 2026-09-15 SEO/security
|
|
// audit — no HSTS, X-Content-Type-Options, X-Frame-Options, or
|
|
// Referrer-Policy were present on any response).
|
|
//
|
|
// Deliberately NOT setting Content-Security-Policy here: the site relies on
|
|
// inline <script> blocks (no nonce/hash setup), inline style="" attributes
|
|
// throughout every page, and an optional analytics script loaded from a
|
|
// runtime-configured host (PUBLIC_UMAMI_URL). A CSP tight enough to matter
|
|
// would need nonces wired through every inline script/style or it will
|
|
// silently break rendering/interactivity in production. Needs its own pass
|
|
// with live testing, not a guess baked in here.
|
|
response.headers.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload');
|
|
response.headers.set('X-Content-Type-Options', 'nosniff');
|
|
response.headers.set('X-Frame-Options', 'DENY');
|
|
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
|
|
|
return response;
|
|
});
|