Options -Indexes -MultiViews

DirectoryIndex index.php index.html

# Shared 404 error document (WrongURL). Subdomain routing below remaps /404.php
# into each module dir, where a thin shim re-includes this root file.
ErrorDocument 404 /404.php

RewriteEngine On

# Stop second-pass rewrite loop after internal routing
RewriteCond %{ENV:REDIRECT_SUBROUTED} ^1$
RewriteRule ^ - [L]

# ─── Brand asset passthrough (host-independent) ───────────────────────────────
# Serves root /assets for /__brand/* on every host so 404.php / fallback screens
# load the WrongURL logo+favicon on gen/s/r subdomains without duplicate assets.
# SUBROUTED stops the restart pass from re-applying subdomain routing.
RewriteRule ^__brand/(.+)$ assets/$1 [E=SUBROUTED:1,L]

# ─── Public root discovery/legal endpoints ───────────────────────────────────
RewriteRule ^(privacy|terms|abuse)\.html$ legal.php?page=$1 [L,QSA]
RewriteRule ^sitemap\.xml$ sitemap.php [L,QSA]
RewriteRule ^robots\.txt$ robots.php [L,QSA]

# ─── Block reserved/system subdomains ─────────────────────────────────────────
RewriteCond %{HTTP_HOST} ^(?:mail|cpanel|webmail|webdisk|autodiscover|ftp)\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteRule ^ - [F,L]

# Block the web installer after installation. Reinstall requires deleting
# install.lock locally, then accessing install.php again.
RewriteCond %{DOCUMENT_ROOT}/install.lock -f
RewriteRule ^install\.php$ - [F,L]

RewriteRule ^redirect-decision\.php$ public/redirect-decision.php [L]

# ─── gen.<base-domain> clean login routing ────────────────────────────────────
# gen.example.com/login.php -> /login
# GET/HEAD only to avoid breaking legacy POST form submissions.
RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{REQUEST_METHOD} ^(?:GET|HEAD)$ [NC]
RewriteRule ^login\.php$ /login [R=302,L]

# gen.example.com with empty path and no query string -> /login
RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{QUERY_STRING} ^$
RewriteRule ^$ /login [R=302,L]

# gen.example.com/login -> <APP_ROOT>/public/login.php
RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteRule ^login/?$ public/login.php [E=SUBROUTED:1,L,QSA]

# ─── s.<base-domain> clean shorten routing ────────────────────────────────────
# s.example.com/shorten -> <APP_ROOT>/statistics/shorten-web.php
RewriteCond %{HTTP_HOST} ^s\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteRule ^shorten/?$ statistics/shorten-web.php [E=SUBROUTED:1,L,QSA]

# ─── Exact subdomain routing, auto DocumentRoot ────────────────────────────────
# gen.<base-domain> -> <APP_ROOT>/public
RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/public -d
RewriteRule ^(.*)$ public/$1 [E=SUBROUTED:1,L,QSA]

# s.<base-domain> -> <APP_ROOT>/statistics
RewriteCond %{HTTP_HOST} ^s\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/statistics -d
RewriteRule ^(.*)$ statistics/$1 [E=SUBROUTED:1,L,QSA]

# r.<base-domain> -> <APP_ROOT>/redirect
RewriteCond %{HTTP_HOST} ^r\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/redirect -d
RewriteRule ^(.*)$ redirect/$1 [E=SUBROUTED:1,L,QSA]

# ─── Missing exact folder: allow existing root files/assets ───────────────────
RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/public !-d
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

RewriteCond %{HTTP_HOST} ^s\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/statistics !-d
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

RewriteCond %{HTTP_HOST} ^r\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/redirect !-d
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# ─── Other wildcard subdomains: allow existing root files/assets ──────────────
RewriteCond %{HTTP_HOST} ^([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{HTTP_HOST} !^(?:gen|s|r)\. [NC]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]

# ─── Missing exact folder fallback to root index.php/index.html ───────────────
RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/public !-d
RewriteCond %{DOCUMENT_ROOT}/index.php -f
RewriteRule ^.*$ index.php [E=SUBROUTED:1,L,QSA]

RewriteCond %{HTTP_HOST} ^gen\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/public !-d
RewriteCond %{DOCUMENT_ROOT}/index.html -f
RewriteRule ^.*$ index.html [E=SUBROUTED:1,L,QSA]

RewriteCond %{HTTP_HOST} ^s\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/statistics !-d
RewriteCond %{DOCUMENT_ROOT}/index.php -f
RewriteRule ^.*$ index.php [E=SUBROUTED:1,L,QSA]

RewriteCond %{HTTP_HOST} ^s\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/statistics !-d
RewriteCond %{DOCUMENT_ROOT}/index.html -f
RewriteRule ^.*$ index.html [E=SUBROUTED:1,L,QSA]

RewriteCond %{HTTP_HOST} ^r\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/redirect !-d
RewriteCond %{DOCUMENT_ROOT}/index.php -f
RewriteRule ^.*$ index.php [E=SUBROUTED:1,L,QSA]

RewriteCond %{HTTP_HOST} ^r\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/redirect !-d
RewriteCond %{DOCUMENT_ROOT}/index.html -f
RewriteRule ^.*$ index.html [E=SUBROUTED:1,L,QSA]

# ─── Other wildcard subdomains → TOKEN/short-code routing ─────────────────────
# Route TOKEN or s-{code} path to redirect engine with sub_id parameter.
# Real files and directories are already handled above (e.g. _meetups/, imgp.php).
RewriteCond %{HTTP_HOST} ^([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{HTTP_HOST} !^(?:gen|s|r)\. [NC]
RewriteCond %{DOCUMENT_ROOT}/index.php -f
RewriteRule ^([a-zA-Z0-9_-]{1,2048})/?$ index.php?sub_id=$1 [E=SUBROUTED:1,L,QSA]

# If no root index exists, return 404 for subdomains
RewriteCond %{HTTP_HOST} ^([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)\.(?:[a-z0-9-]+\.)+[a-z]{2,63}$ [NC]
RewriteCond %{DOCUMENT_ROOT}/index.php !-f
RewriteRule ^ - [R=404,L]

# ─── Block sensitive files ────────────────────────────────────────────────────
<FilesMatch "^(\.env|\.env\..*|composer\.(json|lock)|install\.lock|schema\.sql|\.gitignore|\.gitattributes|phpunit\.xml|phpstan\.neon|\.php-cs-fixer\.dist\.php)$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order deny,allow
        Deny from all
    </IfModule>
</FilesMatch>

# Block dot-files except .htaccess and .well-known/
<FilesMatch "^\.(?!htaccess|well-known)">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order deny,allow
        Deny from all
    </IfModule>
</FilesMatch>

# Block internal PHP utility files
<FilesMatch "^(env|connection_pdo|Base64URL|Mobile_Detect|ip_address|login_throttle|imgp_handler)\.php$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order deny,allow
        Deny from all
    </IfModule>
</FilesMatch>

# ─── Security headers ─────────────────────────────────────────────────────────
<IfModule mod_headers.c>
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "same-origin"
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()"
    Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
    Header always set Pragma "no-cache"
    Header always set Expires "0"
    Header always unset X-Powered-By
    Header always unset Server
</IfModule>

# Runtime PHP logging path must be configured in cPanel MultiPHP INI Editor or .user.ini.