Redirect HTTP and www to HTTPS in .htaccess
Force all traffic to HTTPS using .htaccess mod_rewrite rules.
Redirect HTTP to HTTPS (Any Domain)
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Redirect Both HTTP and www to HTTPS Non-www
RewriteEngine On
# Redirect www to non-www
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
# Redirect HTTP to HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Redirect www to HTTPS www
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://www.%1%{REQUEST_URI} [R=301,L]
Notes
- Place these rules at the top of
.htaccess, before WordPress or other rewrite rules.
- Use
R=301 (permanent) for production, R=302 (temporary) while testing.
- Test with
curl -I http://yourdomain.com to verify the redirect chain.
Change Apache's Main Listening IP Address
How to configure Apache to listen on a new IP address, typically needed when the server's IP changes or you add a second IP.
Step 1 — Add a VirtualHost Config File
Create a config file named after the new IP in the Apache conf directory:
# Debian/Ubuntu: /etc/apache2/conf.d/
# CentOS/RHEL: /etc/httpd/conf.d/
# Example: /etc/apache2/conf.d/162.245.216.135.conf
NameVirtualHost 162.245.216.135:80
Listen 162.245.216.135:80
NameVirtualHost 162.245.216.135:443
Listen 162.245.216.135:443
Step 2 — Update VirtualHost Blocks
Update your VirtualHost config files to use the new IP:
ServerName example.com
DocumentRoot /var/www/html/example.com
Step 3 — Test and Restart Apache
apachectl configtest
sudo systemctl restart apache2
Verify
sudo ss -tlnp | grep apache
curl -H "Host: example.com" http://162.245.216.135/
.htaccess File Reference Guide
The .htaccess file allows per-directory Apache configuration without modifying the main server config. It is placed in the web root or any subdirectory.
Enable mod_rewrite
RewriteEngine On
Force HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
Block Access to a File or Directory
# Block access to wp-config.php
Order Allow,Deny
Deny from all
# Block access to a directory
Order Allow,Deny
Deny from all
Custom Error Pages
ErrorDocument 404 /404.html
ErrorDocument 500 /error.html
Gzip Compression
AddOutputFilterByType DEFLATE text/html text/css application/javascript
AddOutputFilterByType DEFLATE text/plain application/json application/xml
Password-Protect a Directory
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user
Prevent Directory Listing
Options -Indexes
Set PHP Values
php_value upload_max_filesize 64M
php_value post_max_size 64M
php_value memory_limit 256M
php_value max_execution_time 300
Redirect Old URL to New URL
Redirect 301 /old-page.html /new-page.html
Enable Gzip Compression in .htaccess
Gzip compression can reduce page size by 60–80%, significantly improving load time. Enable it via .htaccess on Apache servers.
Enable Deflate Compression
# Compress HTML, CSS, JavaScript, Text, XML, and fonts
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE font/woff2
AddOutputFilterByType DEFLATE application/x-font-ttf
# Remove browser bugs (only needed for old browsers)
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch MSIE !no-gzip !gzip-only-text/html
Header append Vary User-Agent
Verify Compression is Working
curl -H "Accept-Encoding: gzip" -I https://example.com/
# Look for: Content-Encoding: gzip
Alternative — mod_gzip (Older Apache)
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_include mime ^text/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
WordPress Default .htaccess Configuration
The standard .htaccess file for WordPress, which enables pretty permalinks and proper URL routing.
Standard WordPress .htaccess
# BEGIN WordPress
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
WordPress in a Subdirectory
If WordPress is installed in a subdirectory (e.g., /blog), use:
# BEGIN WordPress
RewriteEngine On
RewriteBase /blog/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]
# END WordPress
WordPress Multisite .htaccess
# BEGIN WordPress Multisite
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
# Add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
RewriteRule . index.php [L]
# END WordPress Multisite
Set Browser Cache Expiry Headers in Nginx
Configure long cache expiry for static assets in Nginx to improve site performance via browser caching.
Add Expires Headers in Nginx Virtual Host
server {
# ... other config ...
# Cache images, CSS, JS, and fonts for 1 year
location ~* \.(js|css|png|jpg|jpeg|gif|ico|webp|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
log_not_found off;
access_log off;
}
# Cache HTML for a short time
location ~* \.(html|htm)$ {
expires 1h;
add_header Cache-Control "public";
}
}
Alternative — Match Multiple Extensions
location ~* ^.+\.(ico|css|js|gif|jpe?g|png|woff|woff2|eot|svg|ttf)$ {
expires 1y;
add_header Pragma public;
add_header Cache-Control "public";
}
Verify
curl -I https://example.com/style.css
# Look for: Cache-Control: public, max-age=31536000
Fix CORS Issues in .htaccess
Cross-Origin Resource Sharing (CORS) errors occur when a browser blocks requests between different origins. Add appropriate headers in .htaccess to allow cross-origin access.
Allow All Origins (Simple — Not Recommended for Production)
Header set Access-Control-Allow-Origin "*"
Allow a Specific Origin
Header set Access-Control-Allow-Origin "https://app.example.com"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
Allow Multiple Origins Dynamically
SetEnvIf Origin "^https://(app|api)\.example\.com$" ORIGIN_OK=$0
Header set Access-Control-Allow-Origin "%{ORIGIN_OK}e" env=ORIGIN_OK
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
Handle OPTIONS Preflight Requests
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
.htaccess Rewrite Rules for Common Frameworks
Common .htaccess rewrite rules for routing all requests through a front controller (e.g., for Laravel, Slim, CodeIgniter).
Laravel / Slim Framework
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?path=$1 [NC,L,QSA]
CodeIgniter
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]
Symfony
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ public/index.php [QSA,L]
Remove index.php from URL
RewriteEngine On
RewriteCond %{THE_REQUEST} /index\.php [NC]
RewriteRule ^(.*)index\.php(.*)$ /$1$2 [R=301,L]
Allow Access to .well-known Folder in .htaccess
Some WordPress and CMS .htaccess configurations block access to the .well-known directory, which is required for domain verification (AutoSSL, Let's Encrypt, DCV).
Allow .well-known Access
Add this rule before the WordPress or main rewrite block in .htaccess:
RewriteEngine On
# Allow .well-known and validation files through
RewriteCond %{REQUEST_URI} /[A-F0-9]{32}\.txt$ [OR]
RewriteCond %{REQUEST_URI} /\.well\-known\/pki\-validation
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*) $1 [L]
Alternative — Direct Allow Rule
RewriteEngine On
RewriteRule ^\.well-known/ - [L]
Alternative — Using FilesMatch
Order Allow,Deny
Allow from all
Satisfy Any
Allow Subdirectory Access Within a WordPress Site
WordPress's .htaccess by default routes all requests through index.php, which can block direct access to subdirectories or standalone apps installed in a subdirectory. Here's how to exempt a specific directory.
Exempt a Subdirectory from WordPress Routing
Add this rule before the WordPress block:
RewriteEngine On
RewriteBase /
# Bypass WordPress for /myapp/
RewriteRule ^myapp(/.*)?$ - [L]
# BEGIN WordPress
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
Alternative — For Files Inside the Subdirectory
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} ^/mydirectoryhere/(.*)$
RewriteRule ^.*$ - [L]
Notes
- The
- [L] flag means "do nothing and stop processing rules" — the request is passed through as-is.
- Place exemption rules before the WordPress rewrite block to ensure they take effect first.