File: /var/www/indoadvisory_new/web2/webapp/apache.conf
# Apache Virtual Host Configuration for Indo Advisory
# Indonesian Private Equity Firm - Enterprise VPS Deployment
# Location: /etc/apache2/sites-available/indo-advisory.conf
<VirtualHost *:80>
ServerName indoadvisory.com
ServerAlias www.indoadvisory.com
DocumentRoot /var/www/indo-advisory/public
# Logging
ErrorLog ${APACHE_LOG_DIR}/indo-advisory-error.log
CustomLog ${APACHE_LOG_DIR}/indo-advisory-access.log combined
# Security Headers
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "no-referrer-when-downgrade"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Hide Apache version
ServerTokens Prod
ServerSignature Off
# Enable compression
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
# Don't compress images
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|ico|svg)$ no-gzip dont-vary
# Don't compress archives
SetEnvIfNoCase Request_URI \
\.(?:exe|t?gz|zip|bz2|sit|rar|pdf)$ no-gzip dont-vary
# Compress text, html, javascript, css, xml
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/json
</IfModule>
# Cache static assets
<IfModule mod_expires.c>
ExpiresActive On
# Images
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/gif "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 year"
# CSS and JavaScript
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
# Fonts
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
ExpiresByType application/font-woff2 "access plus 1 year"
# HTML and other dynamic content
ExpiresByType text/html "access plus 1 hour"
ExpiresByType application/json "access plus 1 hour"
</IfModule>
# Static files directory
<Directory "/var/www/indo-advisory/public">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
# Security for static files
<FilesMatch "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|pdf|doc|docx)$">
# Add cache headers
Header set Cache-Control "public, max-age=2592000"
</FilesMatch>
# Prevent access to sensitive files
<FilesMatch "\.(env|config|sql|log|git)$">
Require all denied
</FilesMatch>
</Directory>
# Uploads directory (protected - proxy to Node.js for authentication)
<LocationMatch "^/uploads/">
ProxyPass http://127.0.0.1:3000/uploads/
ProxyPassReverse http://127.0.0.1:3000/uploads/
# Additional security headers
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "DENY"
</LocationMatch>
# API routes (proxy to Node.js with rate limiting)
<LocationMatch "^/api/">
ProxyPass http://127.0.0.1:3000/api/
ProxyPassReverse http://127.0.0.1:3000/api/
# Set proxy headers
ProxyPreserveHost On
ProxyPassReverse http://127.0.0.1:3000/api/
# Rate limiting (requires mod_evasive)
<IfModule mod_evasive24.c>
DOSPageCount 100
DOSPageInterval 1
DOSSiteCount 50
DOSSiteInterval 1
DOSBlockingPeriod 300
</IfModule>
</LocationMatch>
# Admin routes (proxy to Node.js with stricter security)
<LocationMatch "^/admin">
ProxyPass http://127.0.0.1:3000/admin
ProxyPassReverse http://127.0.0.1:3000/admin
# Stricter rate limiting for admin
<IfModule mod_evasive24.c>
DOSPageCount 10
DOSPageInterval 1
DOSSiteCount 20
DOSSiteInterval 1
DOSBlockingPeriod 600
</IfModule>
# IP whitelist (uncomment and configure for production)
# Require ip 192.168.1.0/24
# Require ip 203.0.113.0/24
</LocationMatch>
# Authentication routes (proxy to Node.js with rate limiting)
<LocationMatch "^/auth/">
ProxyPass http://127.0.0.1:3000/auth/
ProxyPassReverse http://127.0.0.1:3000/auth/
# Very strict rate limiting for auth
<IfModule mod_evasive24.c>
DOSPageCount 5
DOSPageInterval 60
DOSSiteCount 10
DOSSiteInterval 60
DOSBlockingPeriod 900
</IfModule>
</LocationMatch>
# Language switcher
<LocationMatch "^/lang/">
ProxyPass http://127.0.0.1:3000/lang/
ProxyPassReverse http://127.0.0.1:3000/lang/
</LocationMatch>
# All other requests (proxy to Node.js)
ProxyPass /static/ !
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# Set proxy headers for all requests
ProxyPreserveHost On
ProxyPassReverse / http://127.0.0.1:3000/
# Block access to sensitive files and directories
<DirectoryMatch "^/var/www/indo-advisory/(\.git|node_modules|logs|migrations|scripts)">
Require all denied
</DirectoryMatch>
<FilesMatch "^\.">
Require all denied
</FilesMatch>
<FilesMatch "\.(env|config|sql|log)$">
Require all denied
</FilesMatch>
# Block common exploit attempts
RedirectMatch 404 ^/(wp-admin|wp-content|wp-includes|phpmyadmin|administrator|xmlrpc\.php)
# Security rules
<IfModule mod_rewrite.c>
RewriteEngine On
# Block suspicious requests
RewriteCond %{QUERY_STRING} (eval\(|javascript:|vbscript:|onclick|onload) [NC,OR]
RewriteCond %{QUERY_STRING} (<script|</script|<iframe|</iframe) [NC,OR]
RewriteCond %{QUERY_STRING} (select.*from|union.*select|insert.*into|delete.*from) [NC]
RewriteRule .* - [F,L]
# Block file injection attempts
RewriteCond %{REQUEST_URI} \.\./
RewriteRule .* - [F,L]
# Force HTTPS (uncomment after SSL setup)
# RewriteCond %{HTTPS} off
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
# Additional security with mod_security (if available)
<IfModule mod_security2.c>
SecRuleEngine On
SecDefaultAction "phase:2,deny,log,status:406"
# Basic rules
SecRule ARGS "@detectSQLi" \
"id:1001,phase:2,block,msg:'SQL Injection Attack',logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'"
SecRule ARGS "@detectXSS" \
"id:1002,phase:2,block,msg:'XSS Attack',logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'"
</IfModule>
</VirtualHost>
# SSL Configuration (uncomment after obtaining SSL certificate)
# <VirtualHost *:443>
# ServerName indoadvisory.com
# ServerAlias www.indoadvisory.com
# DocumentRoot /var/www/indo-advisory/public
#
# # SSL Configuration
# SSLEngine on
# SSLCertificateFile /etc/letsencrypt/live/indoadvisory.com/fullchain.pem
# SSLCertificateKeyFile /etc/letsencrypt/live/indoadvisory.com/privkey.pem
#
# # SSL Security Settings
# SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
# SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
# SSLHonorCipherOrder on
# SSLCompression off
#
# # HSTS Header
# Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
#
# # Include all configurations from HTTP virtual host above
# # (Copy all Directory, Location, and Proxy configurations)
#
# # Logging for HTTPS
# ErrorLog ${APACHE_LOG_DIR}/indo-advisory-ssl-error.log
# CustomLog ${APACHE_LOG_DIR}/indo-advisory-ssl-access.log combined
# </VirtualHost>
# Redirect HTTP to HTTPS (uncomment after SSL setup)
# <VirtualHost *:80>
# ServerName indoadvisory.com
# ServerAlias www.indoadvisory.com
#
# # Force redirect to HTTPS
# RewriteEngine On
# RewriteCond %{HTTPS} off
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# </VirtualHost>