# =============================================================================
#  OTS ICT Learning Academy - Portal
#  Apache configuration for cPanel / shared hosting.
# =============================================================================

# Directory listings ------------------------------------------------------------------
<IfModule mod_autoindex.c>
    Options -Indexes -MultiViews
</IfModule>

DirectoryIndex index.php index.html

# Default charset ---------------------------------------------------------------------
AddDefaultCharset UTF-8

# Block direct access to application internals ----------------------------------------
# These folders only hold PHP includes, SQL dumps and logs. The per-folder
# .htaccess files repeat this rule, but the guard here covers sub-directory
# installs and servers that ignore nested overrides.
<IfModule mod_rewrite.c>
    RewriteEngine On

    RewriteRule ^(?:config|includes|database|logs)(?:/|$) - [F,L]
    RewriteRule ^uploads/.*\.(?:php\d?|phtml|phar)$ - [F,L]

    # Clean URLs ----------------------------------------------------------------------
    # Keep .php out of the address bar. Legacy requests that still carry the
    # suffix are redirected to the extensionless form. Only GET and HEAD are
    # redirected so form posts and uploads are never replayed; query strings
    # survive both hops. THE_REQUEST guards keep internal rewrites from
    # redirecting themselves.
    RewriteCond %{THE_REQUEST} \s/+(?:.*/)?index\.php[\s?] [NC]
    RewriteCond %{REQUEST_METHOD} ^(?:GET|HEAD)$ [NC]
    RewriteRule ^(.*/)?index\.php$ /$1 [R=301,L]

    RewriteCond %{THE_REQUEST} \s/+.*\.php[\s?] [NC]
    RewriteCond %{REQUEST_METHOD} ^(?:GET|HEAD)$ [NC]
    RewriteRule ^(.*)\.php$ /$1 [R=301,L,NE]

    # Serve the extensionless URL from its script without changing the address
    # bar. Real files and directories (assets, uploads, folders) are untouched.
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}\.php -f
    RewriteRule ^(.+?)/?$ $1.php [L,QSA]
</IfModule>

# Block sensitive file types ----------------------------------------------------------
<FilesMatch "(?i)\.(sql|log|ini|bak|backup|old|orig|sh|bash|dist|example|md|lock|yml|yaml)$">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order allow,deny
        Deny from all
    </IfModule>
</FilesMatch>

# Never serve dotfiles (.env, .git, .htaccess, .user.ini ...) ---------------------------
<FilesMatch "^\.">
    <IfModule mod_authz_core.c>
        Require all denied
    </IfModule>
    <IfModule !mod_authz_core.c>
        Order allow,deny
        Deny from all
    </IfModule>
</FilesMatch>

# Security headers --------------------------------------------------------------------
# PHP also emits these (see includes/functions.php) so the app stays protected even
# when .htaccess overrides are unavailable. Kept here as defence in depth.
<IfModule mod_headers.c>
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
    Header always set X-Permitted-Cross-Domain-Policies "none"
    Header always set X-XSS-Protection "0"

    # Remove PHP version disclosure where the host allows it.
    Header unset X-Powered-By
    Header always unset X-Powered-By
</IfModule>

# Compression -------------------------------------------------------------------------
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml
    AddOutputFilterByType DEFLATE application/javascript application/json
    AddOutputFilterByType DEFLATE application/xml image/svg+xml
</IfModule>

# Browser caching ---------------------------------------------------------------------
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 1 day"
    ExpiresByType application/javascript "access plus 1 day"
    ExpiresByType image/svg+xml "access plus 7 days"
    ExpiresByType image/png "access plus 7 days"
    ExpiresByType image/jpeg "access plus 7 days"
    ExpiresByType image/webp "access plus 7 days"
    ExpiresByType font/woff2 "access plus 30 days"
</IfModule>

# PHP session hardening (applied when PHP runs as an Apache module) ------------------
<IfModule mod_php.c>
    php_flag display_errors Off
    php_flag session.cookie_httponly On
    php_flag session.use_strict_mode On
    php_flag session.use_only_cookies On
    php_value session.cookie_samesite "Lax"
</IfModule>
<IfModule mod_php7.c>
    php_flag display_errors Off
    php_flag session.cookie_httponly On
    php_flag session.use_strict_mode On
    php_flag session.use_only_cookies On
    php_value session.cookie_samesite "Lax"
</IfModule>
<IfModule mod_php8.c>
    php_flag display_errors Off
    php_flag session.cookie_httponly On
    php_flag session.use_strict_mode On
    php_flag session.use_only_cookies On
    php_value session.cookie_samesite "Lax"
</IfModule>

