Web server & git
Three files that protect the app at the edges: two .htaccess files that tell Apache how to route, secure, and cache requests, and a .gitignore that keeps secrets and installed dependencies out of git history. They are stack-specific to Apache (shared hosting, Hostinger and similar) — skip the htaccess files if you run Nginx.
The two .htaccess files
Section titled “The two .htaccess files”Laravel wants its document root at /public. On hosting where you can set that, only the public file matters. On shared hosting where the web root is the project root, you also need the root file to forward traffic into /public and block .env from the web.
| File | Lands at | Does what |
|---|---|---|
Root .htaccess | project root | Routes traffic into /public, blocks .env, allows vendor/asset paths |
Public .htaccess | /public/.htaccess | Front controller, HTTPS + HSTS, security headers, caching, gzip |
# ============================================================================# 📋 ROOT .HTACCESS TEMPLATE (Laravel)# ============================================================================# Purpose: Route requests to public folder for Laravel apps# Location: Project root (same level as /public, /app, /vendor)# Use when: Shared hosting where public_html points to project root and the# server cannot set document root to /public.# ============================================================================
<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews -Indexes </IfModule>
# ---------------------------------------- # SECURITY: Block .env access # ---------------------------------------- <Files .env> Order Allow,Deny Deny from all </Files>
RewriteEngine On
# Handle Authorization Header (API tokens) RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Remove trailing slashes (SEO/consistency) RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301]
# Front Controller: route to index.php, skip static assets for performance # CUSTOMIZE: add/remove extensions to match your project RewriteCond %{REQUEST_URI} !(\.css|\.js|\.png|\.jpg|\.jpeg|\.gif|robots\.txt|\.ico|\.woff|\.woff2|\.ttf|\.svg|\.webp)$ [NC] RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L]
# Vendor Package Assets (CodeCanyon) # CUSTOMIZE: change "workdo" to your vendor name (e.g. infinitietech, Modules) RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_URI} ^/packages/workdo/ RewriteRule ^packages/workdo/(.*)$ packages/workdo/$1 [L,NC]
# Public Folder Asset Routing # CUSTOMIZE: add folders like landing/, market_assets/, installer/, uploads/ RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_URI} !^/public/ RewriteRule ^(css|assets|market_assets|images|landing|uploads|storage|installer|js|vendor)/(.*)$ public/$1/$2 [L,NC]</IfModule>
# ============================================================================# 📝 PROJECT-SPECIFIC ADDITIONS — add rules below this line# ============================================================================# --- Custom Domain Redirects ---# RewriteCond %{HTTP_HOST} ^old-domain\.com$ [NC]# RewriteRule ^(.*)$ https://new-domain.com/$1 [L,R=301]## --- Additional Vendor Packages ---# RewriteCond %{REQUEST_FILENAME} !-d# RewriteCond %{REQUEST_FILENAME} !-f# RewriteCond %{REQUEST_URI} ^/packages/othervendor/# RewriteRule ^packages/othervendor/(.*)$ packages/othervendor/$1 [L,NC]
# ============================================================================# 📝 USAGE: 1. Back up existing .htaccess 2. Copy here as ".htaccess"# 3. Merge project rules 4. Test: yoursite.com/.env should 403# ============================================================================# ============================================================================# 📋 PUBLIC FOLDER .HTACCESS TEMPLATE (Laravel)# ============================================================================# Location: /public/.htaccess# Features: URL rewriting · HTTPS + HSTS · security headers · browser caching# · gzip compression · ETag config# ============================================================================
# PHP Settings (uncomment if mod_php is installed; may not work on all hosts)#php_value max_execution_time 600#php_value memory_limit 512M#php_value post_max_size 100M#php_value upload_max_filesize 100M
# ----------------------------------------------------------------------------# 💻 URL REWRITING (Front Controller)# ----------------------------------------------------------------------------<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews -Indexes </IfModule>
RewriteEngine On
# Handle Authorization Header (for API tokens) RewriteCond %{HTTP:Authorization} . RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Remove trailing slashes (SEO consistency) RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_URI} (.+)/$ RewriteRule ^ %1 [L,R=301]
# Send all requests to index.php (Laravel front controller) RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L]</IfModule>
# ----------------------------------------------------------------------------# 🔒 SECURITY: HTTPS + HSTS# ----------------------------------------------------------------------------<IfModule mod_headers.c> # HSTS: 1 year, include subdomains, preload eligible Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"</IfModule>
# Force HTTP → HTTPS Redirect<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]</IfModule>
# ----------------------------------------------------------------------------# 🔒 SECURITY: Additional Headers# ----------------------------------------------------------------------------<IfModule mod_headers.c> Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Content-Security-Policy: ⚠️ CUSTOMIZE — add your analytics/support/payment domains Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://fonts.googleapis.com https:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https:; img-src 'self' data: blob: https:; font-src 'self' data: https://fonts.gstatic.com https:; connect-src 'self' https:; frame-src 'self' https:; frame-ancestors 'self'"
# Hide server technology Header unset X-Powered-By</IfModule>
# ----------------------------------------------------------------------------# ⚡ PERFORMANCE: Browser Caching (Expires)# ----------------------------------------------------------------------------<IfModule mod_expires.c> ExpiresActive On ExpiresDefault "access plus 1 week"
ExpiresByType text/html "access plus 0 seconds" ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" ExpiresByType text/javascript "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/webp "access plus 1 month" ExpiresByType image/svg+xml "access plus 1 month" ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType font/woff "access plus 1 year" ExpiresByType font/woff2 "access plus 1 year" ExpiresByType font/ttf "access plus 1 year" ExpiresByType font/otf "access plus 1 year"
ExpiresByType application/json "access plus 0 seconds" ExpiresByType application/xml "access plus 0 seconds"</IfModule>
# ----------------------------------------------------------------------------# ⚡ PERFORMANCE: Cache-Control Headers# ----------------------------------------------------------------------------<IfModule mod_headers.c> # Static assets: long cache, immutable (versioned files) <FilesMatch "\.(css|js|ico|pdf|jpg|jpeg|png|gif|webp|svg|woff|woff2|ttf|otf|eot)$"> Header set Cache-Control "public, max-age=2592000, immutable" </FilesMatch>
# HTML/PHP: no cache (dynamic) <FilesMatch "\.(html|htm|php)$"> Header set Cache-Control "no-cache, no-store, must-revalidate" Header set Pragma "no-cache" </FilesMatch></IfModule>
# ----------------------------------------------------------------------------# ⚡ PERFORMANCE: Gzip Compression (Cloudflare handles this too — keep as fallback)# ----------------------------------------------------------------------------<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/css AddOutputFilterByType DEFLATE application/javascript text/javascript application/x-javascript AddOutputFilterByType DEFLATE application/json application/xml text/xml AddOutputFilterByType DEFLATE application/font-woff application/font-woff2 AddOutputFilterByType DEFLATE image/svg+xml
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|pdf)$ no-gzip dont-vary</IfModule>
# ----------------------------------------------------------------------------# ⚡ PERFORMANCE: ETag Configuration (disable; use Cache-Control/Expires instead)# ----------------------------------------------------------------------------<IfModule mod_headers.c> Header unset ETag</IfModule>FileETag None
# ============================================================================# 📝 USAGE: 1. Back up public/.htaccess 2. Copy here 3. ⚠️ MERGE CSP domains# 4. Verify HTTPS redirect 5. Test headers at securityheaders.com# ============================================================================The .gitignore
Section titled “The .gitignore”Write this before git init so the very first git status is clean — secrets and installed dependencies never enter history. Once a secret or a 200 MB vendor/ tree is committed, removing it is a rewrite-and-force-push ordeal.
# Dependencies/vendor//node_modules/
# Secrets (never commit).env.env.*!.env.example
# Laravel generated/storage/*.key/bootstrap/cache/*.php
# OS & IDE.DS_Store/.idea//.vscode/
# Logs & testing*.log.phpunit.result.cache
# CodeCanyon ZIPs*.zip
# Build — comment out the next line if you use the Build-Locally strategy# /public/build/
# Project vault (credentials)/Admin-Local/1-Project/2-ProjectVault/Ignoring vendor/ keeps it out of history, but you do not delete the shipped tree — a CodeCanyon author’s patched packages stay on disk until the app is verified running. Ignored is not the same as deletable.