113 lines
6.0 KiB
ApacheConf
113 lines
6.0 KiB
ApacheConf
|
|
RewriteEngine on
|
|
|
|
# ----- Canonical host -----
|
|
# msosorg.com now serves this docroot, so consolidate ranking signals: 301
|
|
# every other host — the old temp domain msos.spletnimojster.si, any "www.",
|
|
# and the origin host — to the canonical bare apex over HTTPS. Runs before the
|
|
# HTTPS-force below so the temp domain reaches the canonical in a single hop.
|
|
RewriteCond %{HTTP_HOST} !^msosorg\.com$ [NC]
|
|
RewriteRule ^ https://msosorg.com%{REQUEST_URI} [R=301,L]
|
|
|
|
# ----- Force HTTPS everywhere -----
|
|
# Redirect any plain-http request to https. The two conditions together avoid
|
|
# a redirect loop when TLS is terminated by an upstream proxy (which forwards
|
|
# X-Forwarded-Proto: https while %{HTTPS} may read as off).
|
|
RewriteCond %{HTTPS} !=on
|
|
RewriteCond %{HTTP:X-Forwarded-Proto} !=https
|
|
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
|
|
|
# ----- Language root redirect -----
|
|
# Send the bare root to the default-language homepage. After the canonical-host
|
|
# rule above, this only runs on https://msosorg.com, so it stays relative.
|
|
RewriteRule ^/?$ /en/ [R=301,L]
|
|
|
|
# ----- Defense-in-depth: block dev/sensitive files if they ever ship -----
|
|
# (The deploy already excludes these; this is a safety net. robots.txt,
|
|
# sitemap.xml and site.webmanifest are intentionally NOT blocked.)
|
|
<FilesMatch "(?i)(^code_export\.txt$|\.(sh|py|php|bak|old|log|ini|sql|yml|yaml)$)">
|
|
<IfModule mod_authz_core.c>
|
|
Require all denied
|
|
</IfModule>
|
|
<IfModule !mod_authz_core.c>
|
|
Order allow,deny
|
|
Deny from all
|
|
</IfModule>
|
|
</FilesMatch>
|
|
# Block internal folders and VCS/CI dirs.
|
|
RewriteRule (^|/)\.(git|gitea)(/|$) - [F,L]
|
|
RewriteRule ^(docs|templates|tools)(/|$) - [F,L]
|
|
|
|
|
|
# ============================================================
|
|
# Static-site defaults (added for SEO / performance / 404).
|
|
# Wrapped in <IfModule> guards so a missing module can't 500.
|
|
# Ignored by nginx (harmless). No extra HTTPS/host redirects —
|
|
# the host handles TLS and the rule above handles the root.
|
|
# ============================================================
|
|
|
|
# ----- Custom branded error page -----
|
|
ErrorDocument 404 /404.html
|
|
|
|
# ----- Don't expose directory listings -----
|
|
Options -Indexes
|
|
|
|
# ----- Correct MIME types (WebP, SVG, manifest, fonts) -----
|
|
<IfModule mod_mime.c>
|
|
AddType image/webp .webp
|
|
AddType image/svg+xml .svg
|
|
AddType application/manifest+json .webmanifest
|
|
AddType image/x-icon .ico
|
|
AddType font/woff2 .woff2
|
|
</IfModule>
|
|
|
|
# ----- Gzip / compression for text assets -----
|
|
<IfModule mod_deflate.c>
|
|
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml
|
|
AddOutputFilterByType DEFLATE application/javascript application/json
|
|
AddOutputFilterByType DEFLATE application/xml application/manifest+json
|
|
AddOutputFilterByType DEFLATE image/svg+xml
|
|
</IfModule>
|
|
|
|
# ----- Browser caching -----
|
|
# No file-hashing on this site, so keep CSS/JS short (updates must propagate)
|
|
# and cache images/fonts longer. HTML is always revalidated.
|
|
<IfModule mod_expires.c>
|
|
ExpiresActive On
|
|
ExpiresByType text/html "access plus 0 seconds"
|
|
ExpiresByType text/css "access plus 1 day"
|
|
ExpiresByType application/javascript "access plus 1 day"
|
|
ExpiresByType image/webp "access plus 1 month"
|
|
ExpiresByType image/jpeg "access plus 1 month"
|
|
ExpiresByType image/png "access plus 1 month"
|
|
ExpiresByType image/svg+xml "access plus 1 month"
|
|
ExpiresByType image/x-icon "access plus 1 month"
|
|
ExpiresByType font/woff2 "access plus 1 month"
|
|
</IfModule>
|
|
<IfModule mod_headers.c>
|
|
Header set X-Content-Type-Options "nosniff"
|
|
# HSTS: after the first HTTPS visit, browsers refuse plain HTTP for this
|
|
# host. Conservative 6-month max-age, no preload/includeSubDomains so it
|
|
# stays easy to adjust. (Ignored by browsers over plain HTTP — harmless.)
|
|
Header always set Strict-Transport-Security "max-age=15768000"
|
|
# Clickjacking protection — the site should never be framed by other origins.
|
|
Header always set X-Frame-Options "SAMEORIGIN"
|
|
# Don't leak full page URLs to third parties (analytics, external links).
|
|
Header always set Referrer-Policy "strict-origin-when-cross-origin"
|
|
# Explicitly disable browser features the site doesn't use.
|
|
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), interest-cohort=()"
|
|
# Content-Security-Policy (ENFORCING). Allowlist verified in a browser
|
|
# against a server sending this exact header — every real resource passes:
|
|
# self assets, Google Fonts, Font Awesome (cdnjs), gtag, GA beacons, the
|
|
# Google Forms endpoint (membership + guide feedback), the MailerLite JSONP
|
|
# newsletter, and YouTube (nocookie) embeds. 'unsafe-inline' is required for
|
|
# the in-<head> consent/analytics bootstrap and a few inline handlers; the
|
|
# host allowlists still constrain where scripts load from and where data can
|
|
# be sent (connect/form-action/base-uri/object-src), which is the real value
|
|
# here on top of the site's existing output escaping.
|
|
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://assets.mailerlite.com https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com; font-src 'self' https://fonts.gstatic.com https://cdnjs.cloudflare.com data:; img-src 'self' data: https:; connect-src 'self' https://www.google-analytics.com https://region1.google-analytics.com https://analytics.google.com https://www.googletagmanager.com https://docs.google.com https://assets.mailerlite.com https://cloudflareinsights.com; frame-src https://www.youtube-nocookie.com https://www.youtube.com; frame-ancestors 'self'; base-uri 'self'; form-action 'self' https://docs.google.com; object-src 'none'"
|
|
<FilesMatch "\.html$">
|
|
Header set Cache-Control "no-cache, must-revalidate"
|
|
</FilesMatch>
|
|
</IfModule>
|