2792 lines
60 KiB
Markdown
2792 lines
60 KiB
Markdown
# CyberPanel OpenLiteSpeed Module - Complete Usage Guide
|
|
|
|
**Version:** 2.2.0
|
|
**Last Updated:** December 28, 2025
|
|
**Status:** Production Ready
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Getting Started](#getting-started)
|
|
2. [Header Directives](#1-header-directives)
|
|
3. [Request Header Directives](#2-request-header-directives)
|
|
4. [Environment Variables](#3-environment-variables)
|
|
5. [Access Control](#4-access-control)
|
|
6. [Redirect Directives](#5-redirect-directives)
|
|
7. [Error Documents](#6-error-documents)
|
|
8. [FilesMatch Directives](#7-filesmatch-directives)
|
|
9. [Expires Directives](#8-expires-directives)
|
|
10. [PHP Directives](#9-php-directives)
|
|
11. [Brute Force Protection](#10-brute-force-protection)
|
|
12. [CyberPanel Integration](#cyberpanel-integration)
|
|
13. [Real-World Examples](#real-world-examples)
|
|
14. [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## Getting Started
|
|
|
|
### What is This Module?
|
|
|
|
The CyberPanel OpenLiteSpeed Module brings Apache .htaccess compatibility to OpenLiteSpeed servers. It allows you to use familiar Apache directives without switching web servers.
|
|
|
|
### Quick Start
|
|
|
|
1. **Module is pre-installed** on CyberPanel servers
|
|
2. **Create .htaccess** in your website's public_html directory
|
|
3. **Add directives** from this guide
|
|
4. **Test** using curl or browser
|
|
|
|
### Basic .htaccess Example
|
|
|
|
```apache
|
|
# Security headers
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
|
|
# Enable brute force protection
|
|
BruteForceProtection On
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Header Directives
|
|
|
|
### What Are HTTP Headers?
|
|
|
|
HTTP headers are metadata sent with web responses. They control browser behavior, caching, security, and more.
|
|
|
|
### Supported Operations
|
|
|
|
| Operation | Purpose | Syntax |
|
|
|-----------|---------|--------|
|
|
| **set** | Set header (replaces existing) | `Header set Name "Value"` |
|
|
| **unset** | Remove header | `Header unset Name` |
|
|
| **append** | Append to existing header | `Header append Name "Value"` |
|
|
| **merge** | Add if not present | `Header merge Name "Value"` |
|
|
| **add** | Always add (allows duplicates) | `Header add Name "Value"` |
|
|
|
|
### How to Use
|
|
|
|
#### Basic Security Headers
|
|
|
|
**What it does:** Protects against clickjacking, XSS, and MIME sniffing.
|
|
|
|
```apache
|
|
# Prevent site from being embedded in iframe (clickjacking protection)
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
|
|
# Prevent MIME type sniffing
|
|
Header set X-Content-Type-Options "nosniff"
|
|
|
|
# Enable XSS filter in browsers
|
|
Header set X-XSS-Protection "1; mode=block"
|
|
|
|
# Control referrer information
|
|
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
|
|
|
# Restrict browser features
|
|
Header set Permissions-Policy "geolocation=(), microphone=(), camera=()"
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com | grep -E "X-Frame|X-Content|X-XSS"
|
|
```
|
|
|
|
#### Cache Control Headers
|
|
|
|
**What it does:** Controls how browsers cache your content.
|
|
|
|
```apache
|
|
# Cache for 1 year (static assets)
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
|
|
# No caching (dynamic content)
|
|
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
Header set Pragma "no-cache"
|
|
Header set Expires "0"
|
|
|
|
# Cache for 1 hour
|
|
Header set Cache-Control "max-age=3600, public"
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com/style.css | grep Cache-Control
|
|
```
|
|
|
|
#### CORS Headers
|
|
|
|
**What it does:** Allows cross-origin requests (needed for APIs, fonts, n8n, etc.).
|
|
|
|
```apache
|
|
# Allow all origins
|
|
Header set Access-Control-Allow-Origin "*"
|
|
|
|
# Allow specific origin
|
|
Header set Access-Control-Allow-Origin "https://app.example.com"
|
|
|
|
# Allow specific methods
|
|
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
|
|
|
# Allow specific headers
|
|
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
|
|
|
|
# Allow credentials
|
|
Header set Access-Control-Allow-Credentials "true"
|
|
|
|
# Preflight cache duration
|
|
Header set Access-Control-Max-Age "86400"
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I -H "Origin: https://example.com" https://yourdomain.com/api
|
|
```
|
|
|
|
#### Remove Server Identification
|
|
|
|
**What it does:** Hides server information from attackers.
|
|
|
|
```apache
|
|
Header unset Server
|
|
Header unset X-Powered-By
|
|
Header unset X-LiteSpeed-Tag
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com | grep -E "Server|X-Powered"
|
|
# Should return nothing
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### Via File Manager
|
|
|
|
1. Log into **CyberPanel**
|
|
2. Go to **File Manager**
|
|
3. Navigate to `/home/yourdomain.com/public_html`
|
|
4. Create or edit `.htaccess`
|
|
5. Add header directives
|
|
6. Save and test
|
|
|
|
#### Via SSH
|
|
|
|
```bash
|
|
# Navigate to website directory
|
|
cd /home/yourdomain.com/public_html
|
|
|
|
# Edit .htaccess
|
|
nano .htaccess
|
|
|
|
# Add your headers
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
|
|
# Save (Ctrl+X, Y, Enter)
|
|
|
|
# Test
|
|
curl -I https://yourdomain.com | grep X-Frame
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### WordPress Security Headers
|
|
|
|
```apache
|
|
# WordPress-specific security
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set X-XSS-Protection "1; mode=block"
|
|
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
|
Header unset X-Powered-By
|
|
|
|
# Disable XML-RPC header
|
|
Header unset X-Pingback
|
|
```
|
|
|
|
#### n8n CORS Configuration
|
|
|
|
```apache
|
|
# Allow n8n webhooks
|
|
Header set Access-Control-Allow-Origin "https://your-n8n-instance.com"
|
|
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
|
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
|
|
Header set Access-Control-Allow-Credentials "true"
|
|
```
|
|
|
|
#### API Response Headers
|
|
|
|
```apache
|
|
# JSON API headers
|
|
Header set Content-Type "application/json; charset=utf-8"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set Access-Control-Allow-Origin "*"
|
|
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Request Header Directives
|
|
|
|
### What Are Request Headers?
|
|
|
|
Request headers are sent FROM the client TO the server. This feature lets you modify or add headers before they reach your PHP application.
|
|
|
|
### How It Works
|
|
|
|
Since OpenLiteSpeed's LSIAPI doesn't support direct request header modification, these are implemented as **environment variables** accessible in PHP via `$_SERVER`.
|
|
|
|
### Supported Operations
|
|
|
|
| Operation | Syntax | Result |
|
|
|-----------|--------|--------|
|
|
| **set** | `RequestHeader set Name "Value"` | `$_SERVER['HTTP_NAME']` |
|
|
| **unset** | `RequestHeader unset Name` | Header removed |
|
|
|
|
### How to Use
|
|
|
|
#### SSL/HTTPS Detection (Behind Proxy)
|
|
|
|
**What it does:** Tells your application the request came via HTTPS (when behind Cloudflare, nginx proxy, etc.).
|
|
|
|
```apache
|
|
# Set HTTPS protocol headers
|
|
RequestHeader set X-Forwarded-Proto "https"
|
|
RequestHeader set X-Forwarded-SSL "on"
|
|
RequestHeader set X-Real-IP "%{REMOTE_ADDR}e"
|
|
```
|
|
|
|
**PHP Usage:**
|
|
```php
|
|
<?php
|
|
// Detect HTTPS
|
|
$proto = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? 'http';
|
|
$isHttps = ($proto === 'https');
|
|
|
|
// Get real IP
|
|
$realIp = $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'];
|
|
|
|
// Force HTTPS redirect
|
|
if (!$isHttps && $_SERVER['REQUEST_METHOD'] !== 'OPTIONS') {
|
|
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
|
|
exit;
|
|
}
|
|
?>
|
|
```
|
|
|
|
#### Application Environment Identification
|
|
|
|
**What it does:** Tags requests with environment information.
|
|
|
|
```apache
|
|
# Identify environment
|
|
RequestHeader set X-Environment "production"
|
|
RequestHeader set X-Server-Location "us-east-1"
|
|
RequestHeader set X-Request-Start "%{REQUEST_TIME}e"
|
|
```
|
|
|
|
**PHP Usage:**
|
|
```php
|
|
<?php
|
|
$env = $_SERVER['HTTP_X_ENVIRONMENT'] ?? 'development';
|
|
$location = $_SERVER['HTTP_X_SERVER_LOCATION'] ?? 'unknown';
|
|
|
|
if ($env === 'production') {
|
|
ini_set('display_errors', 0);
|
|
error_reporting(E_ALL & ~E_DEPRECATED);
|
|
}
|
|
?>
|
|
```
|
|
|
|
#### Custom Backend Headers
|
|
|
|
**What it does:** Passes custom information to your application.
|
|
|
|
```apache
|
|
# Custom application headers
|
|
RequestHeader set X-API-Version "v2"
|
|
RequestHeader set X-Feature-Flags "new-ui,beta-features"
|
|
RequestHeader set X-Client-Type "web"
|
|
```
|
|
|
|
**PHP Usage:**
|
|
```php
|
|
<?php
|
|
$apiVersion = $_SERVER['HTTP_X_API_VERSION'] ?? 'v1';
|
|
$features = explode(',', $_SERVER['HTTP_X_FEATURE_FLAGS'] ?? '');
|
|
$clientType = $_SERVER['HTTP_X_CLIENT_TYPE'] ?? 'unknown';
|
|
|
|
if (in_array('beta-features', $features)) {
|
|
// Enable beta features
|
|
}
|
|
?>
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### For WordPress Behind Cloudflare
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/.htaccess
|
|
RequestHeader set X-Forwarded-Proto "https"
|
|
RequestHeader set X-Forwarded-SSL "on"
|
|
|
|
# WordPress will now correctly detect HTTPS
|
|
```
|
|
|
|
**Verify in WordPress:**
|
|
```php
|
|
// Add to wp-config.php if needed
|
|
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
|
|
$_SERVER['HTTPS'] = 'on';
|
|
}
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Cloudflare + WordPress
|
|
|
|
```apache
|
|
RequestHeader set X-Forwarded-Proto "https"
|
|
RequestHeader set X-Forwarded-SSL "on"
|
|
RequestHeader set X-Real-IP "%{REMOTE_ADDR}e"
|
|
```
|
|
|
|
#### Laravel Behind Load Balancer
|
|
|
|
```apache
|
|
RequestHeader set X-Forwarded-Proto "https"
|
|
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}e"
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Environment Variables
|
|
|
|
### What Are Environment Variables?
|
|
|
|
Environment variables are key-value pairs accessible in your PHP application. They're useful for configuration, feature flags, and conditional logic.
|
|
|
|
### Supported Directives
|
|
|
|
| Directive | Purpose | Syntax |
|
|
|-----------|---------|--------|
|
|
| **SetEnv** | Set static variable | `SetEnv NAME value` |
|
|
| **SetEnvIf** | Conditional set (case-sensitive) | `SetEnvIf attribute regex VAR=value` |
|
|
| **SetEnvIfNoCase** | Conditional set (case-insensitive) | `SetEnvIfNoCase attribute regex VAR=value` |
|
|
| **BrowserMatch** | Detect browser | `BrowserMatch regex VAR=value` |
|
|
|
|
### How to Use
|
|
|
|
#### Static Configuration Variables
|
|
|
|
**What it does:** Sets application configuration accessible in PHP.
|
|
|
|
```apache
|
|
# Application settings
|
|
SetEnv APPLICATION_ENV production
|
|
SetEnv DB_HOST localhost
|
|
SetEnv DB_NAME myapp_db
|
|
SetEnv API_ENDPOINT https://api.example.com
|
|
SetEnv FEATURE_FLAG_NEW_UI enabled
|
|
SetEnv DEBUG_MODE off
|
|
```
|
|
|
|
**PHP Usage:**
|
|
```php
|
|
<?php
|
|
$env = $_SERVER['APPLICATION_ENV'] ?? 'development';
|
|
$dbHost = $_SERVER['DB_HOST'] ?? 'localhost';
|
|
$apiEndpoint = $_SERVER['API_ENDPOINT'] ?? '';
|
|
$newUiEnabled = ($_SERVER['FEATURE_FLAG_NEW_UI'] ?? 'off') === 'enabled';
|
|
|
|
if ($newUiEnabled) {
|
|
require 'templates/new-ui.php';
|
|
} else {
|
|
require 'templates/old-ui.php';
|
|
}
|
|
?>
|
|
```
|
|
|
|
#### Conditional Variables (SetEnvIf)
|
|
|
|
**What it does:** Sets variables based on request properties.
|
|
|
|
##### Supported Conditions
|
|
|
|
- `Request_URI` - URL path
|
|
- `Request_Method` - HTTP method (GET, POST, etc.)
|
|
- `User-Agent` - Browser/client identifier
|
|
- `Host` - Domain name
|
|
- `Referer` - Referrer URL
|
|
- `Query_String` - URL parameters
|
|
- `Remote_Addr` - Client IP address
|
|
|
|
**Examples:**
|
|
|
|
```apache
|
|
# Detect API requests
|
|
SetEnvIf Request_URI "^/api/" IS_API_REQUEST=1
|
|
|
|
# Detect POST requests
|
|
SetEnvIf Request_Method "POST" IS_POST_REQUEST=1
|
|
|
|
# Detect specific domain
|
|
SetEnvIf Host "^beta\." IS_BETA_SITE=1
|
|
|
|
# Detect search queries
|
|
SetEnvIf Query_String "search=" HAS_SEARCH=1
|
|
|
|
# Detect local development
|
|
SetEnvIf Remote_Addr "^127\.0\.0\.1$" IS_LOCAL=1
|
|
```
|
|
|
|
**PHP Usage:**
|
|
```php
|
|
<?php
|
|
if (!empty($_SERVER['IS_API_REQUEST'])) {
|
|
header('Content-Type: application/json');
|
|
$output = json_encode($data);
|
|
} else {
|
|
header('Content-Type: text/html');
|
|
$output = render_html($data);
|
|
}
|
|
|
|
if (!empty($_SERVER['IS_BETA_SITE'])) {
|
|
// Enable experimental features
|
|
define('BETA_FEATURES', true);
|
|
}
|
|
?>
|
|
```
|
|
|
|
#### Browser Detection
|
|
|
|
**What it does:** Identifies the user's browser for compatibility handling.
|
|
|
|
```apache
|
|
# Case-insensitive browser detection
|
|
SetEnvIfNoCase User-Agent "mobile|android|iphone|ipad" IS_MOBILE=1
|
|
SetEnvIfNoCase User-Agent "bot|crawler|spider|scraper" IS_BOT=1
|
|
SetEnvIfNoCase User-Agent "MSIE|Trident" IS_IE=1
|
|
|
|
# Specific browser matching
|
|
BrowserMatch "Chrome" IS_CHROME=1
|
|
BrowserMatch "Firefox" IS_FIREFOX=1
|
|
BrowserMatch "Safari" IS_SAFARI=1
|
|
BrowserMatch "Edge" IS_EDGE=1
|
|
```
|
|
|
|
**PHP Usage:**
|
|
```php
|
|
<?php
|
|
if (!empty($_SERVER['IS_MOBILE'])) {
|
|
require 'mobile-layout.php';
|
|
} else {
|
|
require 'desktop-layout.php';
|
|
}
|
|
|
|
if (!empty($_SERVER['IS_BOT'])) {
|
|
// Serve cached version to bots
|
|
serve_cached_page();
|
|
exit;
|
|
}
|
|
|
|
if (!empty($_SERVER['IS_IE'])) {
|
|
echo '<div class="browser-warning">Please use a modern browser</div>';
|
|
}
|
|
?>
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### Environment-Specific Configuration
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Production settings
|
|
SetEnv APPLICATION_ENV production
|
|
SetEnv DEBUG_MODE off
|
|
SetEnv CACHE_ENABLED on
|
|
|
|
# Database connection
|
|
SetEnv DB_HOST localhost
|
|
SetEnv DB_NAME wp_database
|
|
|
|
# Feature flags
|
|
SetEnv ENABLE_CDN on
|
|
SetEnv ENABLE_CACHE on
|
|
```
|
|
|
|
**WordPress Usage (wp-config.php):**
|
|
```php
|
|
<?php
|
|
// Use environment variables
|
|
define('WP_ENV', $_SERVER['APPLICATION_ENV'] ?? 'production');
|
|
define('WP_DEBUG', ($_SERVER['DEBUG_MODE'] ?? 'off') === 'on');
|
|
|
|
if (WP_DEBUG) {
|
|
define('WP_DEBUG_LOG', true);
|
|
define('WP_DEBUG_DISPLAY', true);
|
|
}
|
|
?>
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Mobile Detection + Redirect
|
|
|
|
```apache
|
|
# Detect mobile users
|
|
SetEnvIfNoCase User-Agent "mobile|android|iphone" IS_MOBILE=1
|
|
|
|
# Redirect mobile to subdomain (using PHP)
|
|
```
|
|
|
|
**PHP redirect:**
|
|
```php
|
|
<?php
|
|
if (!empty($_SERVER['IS_MOBILE']) && strpos($_SERVER['HTTP_HOST'], 'm.') !== 0) {
|
|
header('Location: https://m.example.com' . $_SERVER['REQUEST_URI']);
|
|
exit;
|
|
}
|
|
?>
|
|
```
|
|
|
|
#### API Rate Limiting Preparation
|
|
|
|
```apache
|
|
# Tag API requests
|
|
SetEnvIf Request_URI "^/api/" IS_API=1
|
|
SetEnvIf Request_Method "POST" IS_POST=1
|
|
```
|
|
|
|
**PHP rate limiting:**
|
|
```php
|
|
<?php
|
|
if (!empty($_SERVER['IS_API'])) {
|
|
// Apply API rate limiting
|
|
check_api_rate_limit($_SERVER['REMOTE_ADDR']);
|
|
}
|
|
?>
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Access Control
|
|
|
|
### What is Access Control?
|
|
|
|
Access control restricts who can access your website based on IP addresses. Perfect for staging sites, admin panels, or development environments.
|
|
|
|
### Directives
|
|
|
|
| Directive | Syntax | Description |
|
|
|-----------|--------|-------------|
|
|
| **Order** | `Order deny,allow` or `Order allow,deny` | Set evaluation order |
|
|
| **Allow** | `Allow from IP/CIDR` | Allow specific IP |
|
|
| **Deny** | `Deny from IP/CIDR` | Deny specific IP |
|
|
|
|
### Supported IP Formats
|
|
|
|
- **Single IP:** `192.168.1.100`
|
|
- **CIDR Range:** `192.168.1.0/24` (entire subnet)
|
|
- **Large Ranges:** `10.0.0.0/8` (entire class)
|
|
- **IPv6:** `2001:db8::/32`
|
|
- **Wildcard:** `all` (everyone)
|
|
|
|
### How Order Works
|
|
|
|
#### Order deny,allow
|
|
|
|
1. Check **Deny** list first
|
|
2. Then check **Allow** list
|
|
3. **Allow overrides Deny**
|
|
4. Default: **DENY** if not in either list
|
|
|
|
```apache
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 192.168.1.100
|
|
# Result: Only 192.168.1.100 can access
|
|
```
|
|
|
|
#### Order allow,deny
|
|
|
|
1. Check **Allow** list first
|
|
2. Then check **Deny** list
|
|
3. **Deny overrides Allow**
|
|
4. Default: **ALLOW** if not in either list
|
|
|
|
```apache
|
|
Order allow,deny
|
|
Allow from all
|
|
Deny from 192.168.1.100
|
|
# Result: Everyone except 192.168.1.100 can access
|
|
```
|
|
|
|
### How to Use
|
|
|
|
#### Block All Except Specific IPs (Recommended for Staging)
|
|
|
|
```apache
|
|
# Only allow office IP and VPN
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 203.0.113.50 # Office IP
|
|
Allow from 192.168.1.0/24 # Office LAN
|
|
Allow from 10.8.0.0/24 # VPN range
|
|
```
|
|
|
|
**Use case:** Development/staging sites, admin areas
|
|
|
|
**Testing:**
|
|
```bash
|
|
# From allowed IP
|
|
curl https://staging.example.com
|
|
# Should work
|
|
|
|
# From other IP
|
|
curl https://staging.example.com
|
|
# Should get 403 Forbidden
|
|
```
|
|
|
|
#### Allow All Except Specific IPs
|
|
|
|
```apache
|
|
# Block known attackers
|
|
Order allow,deny
|
|
Allow from all
|
|
Deny from 198.51.100.50 # Banned IP
|
|
Deny from 203.0.113.0/24 # Banned subnet
|
|
```
|
|
|
|
**Use case:** Blocking spam IPs, attack sources
|
|
|
|
#### Protect Admin Directory
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/admin/.htaccess
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 192.168.1.0/24 # Office network
|
|
Allow from 203.0.113.100 # Your home IP
|
|
```
|
|
|
|
**Use case:** WordPress wp-admin protection
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### Protect Staging Site
|
|
|
|
1. Create subdomain `staging.yourdomain.com` in CyberPanel
|
|
2. Navigate to `/home/staging.yourdomain.com/public_html`
|
|
3. Create `.htaccess`:
|
|
|
|
```apache
|
|
# Staging site - Office only
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from YOUR.OFFICE.IP.HERE
|
|
Allow from YOUR.HOME.IP.HERE
|
|
```
|
|
|
|
4. Test:
|
|
```bash
|
|
# Get your IP
|
|
curl ifconfig.me
|
|
|
|
# Test access
|
|
curl -I https://staging.yourdomain.com
|
|
# Should see 403 if not allowed
|
|
```
|
|
|
|
#### Protect WordPress Admin
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/wp-admin/.htaccess
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 203.0.113.50 # Your IP
|
|
```
|
|
|
|
**Important:** This creates TWO layers of protection:
|
|
1. IP restriction (from .htaccess)
|
|
2. Login authentication (from WordPress)
|
|
|
|
#### Protect CyberPanel Access
|
|
|
|
```apache
|
|
# In /usr/local/CyberCP/public/.htaccess (if web accessible)
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 127.0.0.1 # localhost
|
|
Allow from 192.168.1.0/24 # Your network
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Development Environment
|
|
|
|
```apache
|
|
# Dev site - developers only
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 192.168.1.0/24 # Office LAN
|
|
Allow from 10.8.0.0/24 # VPN
|
|
Allow from 203.0.113.50 # Lead developer home
|
|
```
|
|
|
|
#### Geographic Restriction
|
|
|
|
```apache
|
|
# Block specific countries (you need to maintain IP list)
|
|
Order allow,deny
|
|
Allow from all
|
|
Deny from 198.51.100.0/24 # Country X subnet
|
|
Deny from 203.0.113.0/24 # Country Y subnet
|
|
```
|
|
|
|
#### API Endpoint Protection
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/api/.htaccess
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 10.0.0.0/8 # Internal network
|
|
Allow from 172.16.0.0/12 # Private network
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
**Problem:** Getting 403 even from allowed IP
|
|
|
|
**Solution:**
|
|
1. Check your actual IP: `curl ifconfig.me`
|
|
2. Verify CIDR: `192.168.1.0/24` covers `192.168.1.1` to `192.168.1.254`
|
|
3. Check logs: `tail -f /usr/local/lsws/logs/error.log`
|
|
|
|
**Problem:** Access control not working
|
|
|
|
**Solution:**
|
|
1. Verify module loaded: `ls -la /usr/local/lsws/modules/cyberpanel_ols.so`
|
|
2. Check .htaccess permissions: `chmod 644 .htaccess`
|
|
3. Restart OpenLiteSpeed: `/usr/local/lsws/bin/lswsctrl restart`
|
|
|
|
---
|
|
|
|
## 5. Redirect Directives
|
|
|
|
### What Are Redirects?
|
|
|
|
Redirects tell browsers to go to a different URL. Essential for SEO, site migrations, and URL structure changes.
|
|
|
|
### Directives
|
|
|
|
| Directive | Syntax | Use Case |
|
|
|-----------|--------|----------|
|
|
| **Redirect** | `Redirect [code] /old /new` | Simple path redirects |
|
|
| **RedirectMatch** | `RedirectMatch [code] regex target` | Pattern-based redirects |
|
|
|
|
### Status Codes
|
|
|
|
| Code | Name | When to Use |
|
|
|------|------|-------------|
|
|
| **301** | Permanent | SEO-friendly, URL has moved forever |
|
|
| **302** | Temporary | URL temporarily moved, may change back |
|
|
| **303** | See Other | Redirect after POST (form submission) |
|
|
| **410** | Gone | Resource permanently deleted |
|
|
|
|
### How to Use
|
|
|
|
#### Simple Redirects
|
|
|
|
**What it does:** Redirects one path to another.
|
|
|
|
```apache
|
|
# Old page to new page
|
|
Redirect 301 /old-page.html /new-page.html
|
|
|
|
# Old directory to new directory
|
|
Redirect 301 /old-blog /blog
|
|
|
|
# Use keywords instead of codes
|
|
Redirect permanent /old-url /new-url
|
|
Redirect temp /maintenance /coming-soon
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com/old-page.html
|
|
# Should show: HTTP/1.1 301 Moved Permanently
|
|
# Location: https://yourdomain.com/new-page.html
|
|
```
|
|
|
|
#### Force HTTPS
|
|
|
|
**What it does:** Redirects HTTP to HTTPS.
|
|
|
|
```apache
|
|
# Redirect HTTP to HTTPS
|
|
Redirect 301 / https://yourdomain.com/
|
|
```
|
|
|
|
**Better Alternative (checks if already HTTPS):**
|
|
```apache
|
|
SetEnvIf Request_URI ".*" IS_HTTP=1
|
|
# Use with PHP to avoid redirect loop
|
|
```
|
|
|
|
**PHP solution:**
|
|
```php
|
|
<?php
|
|
if ($_SERVER['REQUEST_SCHEME'] !== 'https') {
|
|
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301);
|
|
exit;
|
|
}
|
|
?>
|
|
```
|
|
|
|
#### Force WWW or Non-WWW
|
|
|
|
**What it does:** Standardizes domain format for SEO.
|
|
|
|
```apache
|
|
# Force www
|
|
Redirect 301 / https://www.yourdomain.com/
|
|
|
|
# Force non-www (use RedirectMatch)
|
|
RedirectMatch 301 ^(.*)$ https://yourdomain.com$1
|
|
```
|
|
|
|
#### Pattern-Based Redirects (RedirectMatch)
|
|
|
|
**What it does:** Uses regex to match and redirect URLs.
|
|
|
|
```apache
|
|
# Blog restructuring
|
|
RedirectMatch 301 ^/blog/(.*)$ /news/$1
|
|
# /blog/post-1 → /news/post-1
|
|
|
|
# Product ID migration
|
|
RedirectMatch 301 ^/product-([0-9]+)$ /item/$1
|
|
# /product-123 → /item/123
|
|
|
|
# Year/month/title to title
|
|
RedirectMatch 301 ^/blog/([0-9]{4})/([0-9]{2})/(.*)$ /articles/$3
|
|
# /blog/2024/12/my-post → /articles/my-post
|
|
|
|
# Category reorganization
|
|
RedirectMatch 301 ^/category/(.*)$ /topics/$1
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com/blog/my-post
|
|
# Should redirect to /news/my-post
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### Site Migration (Old Domain to New)
|
|
|
|
```apache
|
|
# In old site's .htaccess
|
|
Redirect 301 / https://new-domain.com/
|
|
```
|
|
|
|
**Steps:**
|
|
1. Keep old domain active in CyberPanel
|
|
2. Add redirect to `/home/old-domain.com/public_html/.htaccess`
|
|
3. Monitor traffic migration
|
|
4. After 6 months, can delete old domain
|
|
|
|
#### WordPress Permalink Change
|
|
|
|
**Scenario:** Changed permalinks from `/?p=123` to `/blog/post-title`
|
|
|
|
```apache
|
|
# WordPress handles this automatically, but for custom:
|
|
RedirectMatch 301 ^/\?p=([0-9]+)$ /blog/post-$1
|
|
```
|
|
|
|
#### E-commerce URL Update
|
|
|
|
```apache
|
|
# Old: /products/view/123
|
|
# New: /shop/product-123
|
|
|
|
RedirectMatch 301 ^/products/view/([0-9]+)$ /shop/product-$1
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Complete Site Redesign
|
|
|
|
```apache
|
|
# Redirect old structure to new
|
|
RedirectMatch 301 ^/about-us$ /about
|
|
RedirectMatch 301 ^/contact-us$ /contact
|
|
RedirectMatch 301 ^/services/(.*)$ /solutions/$1
|
|
RedirectMatch 301 ^/blog/(.*)$ /news/$1
|
|
```
|
|
|
|
#### Affiliate Link Management
|
|
|
|
```apache
|
|
# Short URLs for affiliate links
|
|
Redirect 302 /go/amazon https://amazon.com/your-affiliate-link
|
|
Redirect 302 /go/product https://example.com/long-url-here
|
|
```
|
|
|
|
#### Seasonal Campaigns
|
|
|
|
```apache
|
|
# Temporary campaign redirect
|
|
Redirect 302 /sale /christmas-sale-2025
|
|
Redirect 302 /promo /black-friday
|
|
```
|
|
|
|
#### Remove .html Extensions (SEO)
|
|
|
|
```apache
|
|
# Old: /page.html
|
|
# New: /page
|
|
|
|
RedirectMatch 301 ^/(.*)/index\.html$ /$1/
|
|
RedirectMatch 301 ^/(.*)[^/]\.html$ /$1
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
**Problem:** Redirect loop
|
|
|
|
**Solution:** Check for conflicting rules:
|
|
```apache
|
|
# BAD - Creates loop
|
|
Redirect 301 / https://example.com/
|
|
Redirect 301 / https://www.example.com/
|
|
|
|
# GOOD - Use one or the other
|
|
Redirect 301 / https://www.example.com/
|
|
```
|
|
|
|
**Problem:** Redirect not working
|
|
|
|
**Solution:**
|
|
1. Clear browser cache (redirects are cached!)
|
|
2. Test with curl: `curl -I https://yoursite.com/old-page`
|
|
3. Check .htaccess syntax
|
|
4. Restart OpenLiteSpeed
|
|
|
|
---
|
|
|
|
## 6. Error Documents
|
|
|
|
### What Are Error Documents?
|
|
|
|
Custom error pages shown when errors occur (404 Not Found, 500 Internal Server Error, etc.).
|
|
|
|
### Supported Error Codes
|
|
|
|
| Code | Error | When It Happens |
|
|
|------|-------|-----------------|
|
|
| **400** | Bad Request | Malformed request |
|
|
| **401** | Unauthorized | Authentication required |
|
|
| **403** | Forbidden | Access denied |
|
|
| **404** | Not Found | Page doesn't exist |
|
|
| **500** | Internal Server Error | Server-side error |
|
|
| **502** | Bad Gateway | Proxy/backend error |
|
|
| **503** | Service Unavailable | Server overloaded/maintenance |
|
|
|
|
### Syntax
|
|
|
|
```apache
|
|
ErrorDocument <code> <document>
|
|
```
|
|
|
|
### How to Use
|
|
|
|
#### HTML Error Pages
|
|
|
|
**What it does:** Shows custom-designed error pages.
|
|
|
|
```apache
|
|
# Custom error pages
|
|
ErrorDocument 404 /errors/404.html
|
|
ErrorDocument 500 /errors/500.html
|
|
ErrorDocument 403 /errors/403.html
|
|
ErrorDocument 503 /errors/maintenance.html
|
|
```
|
|
|
|
**Create error pages:**
|
|
|
|
```bash
|
|
mkdir -p /home/yourdomain.com/public_html/errors
|
|
```
|
|
|
|
**404.html example:**
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Page Not Found</title>
|
|
<style>
|
|
body { font-family: Arial; text-align: center; padding: 50px; }
|
|
h1 { color: #e74c3c; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>404 - Page Not Found</h1>
|
|
<p>The page you're looking for doesn't exist.</p>
|
|
<a href="/">Go to Homepage</a>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl https://yourdomain.com/nonexistent-page
|
|
# Should show your custom 404 page
|
|
```
|
|
|
|
#### Inline Messages
|
|
|
|
**What it does:** Shows simple text message.
|
|
|
|
```apache
|
|
ErrorDocument 403 "Access Denied - Contact Administrator"
|
|
ErrorDocument 404 "Page Not Found - Please check the URL"
|
|
```
|
|
|
|
#### WordPress-Friendly Error Pages
|
|
|
|
**What it does:** Routes errors through WordPress.
|
|
|
|
```apache
|
|
# Let WordPress handle 404s
|
|
ErrorDocument 404 /index.php?error=404
|
|
```
|
|
|
|
**WordPress theme (404.php):**
|
|
```php
|
|
<?php
|
|
// Custom 404 page design
|
|
get_header();
|
|
?>
|
|
<h1>Page Not Found</h1>
|
|
<p>Sorry, this page doesn't exist.</p>
|
|
<?php
|
|
get_footer();
|
|
?>
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### Setup Custom Error Pages
|
|
|
|
**Step 1:** Create error directory
|
|
```bash
|
|
cd /home/yourdomain.com/public_html
|
|
mkdir errors
|
|
cd errors
|
|
```
|
|
|
|
**Step 2:** Create error page files
|
|
```bash
|
|
nano 404.html
|
|
# Add custom HTML
|
|
# Save (Ctrl+X, Y, Enter)
|
|
|
|
nano 500.html
|
|
# Add custom HTML
|
|
# Save
|
|
```
|
|
|
|
**Step 3:** Configure .htaccess
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/.htaccess
|
|
ErrorDocument 404 /errors/404.html
|
|
ErrorDocument 500 /errors/500.html
|
|
ErrorDocument 403 /errors/403.html
|
|
```
|
|
|
|
**Step 4:** Test
|
|
```bash
|
|
curl https://yourdomain.com/test-404
|
|
```
|
|
|
|
#### Maintenance Page
|
|
|
|
```apache
|
|
# During maintenance
|
|
ErrorDocument 503 /maintenance.html
|
|
```
|
|
|
|
**maintenance.html:**
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Maintenance</title>
|
|
<meta http-equiv="refresh" content="30">
|
|
<style>
|
|
body { font-family: Arial; text-align: center; padding: 100px; }
|
|
h1 { color: #3498db; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>We'll be right back!</h1>
|
|
<p>Our site is undergoing maintenance.</p>
|
|
<p>Expected completion: 2 hours</p>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
**Trigger maintenance mode:**
|
|
```bash
|
|
# Temporarily disable PHP
|
|
mv index.php index.php.bak
|
|
# Site will show 503
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Professional 404 Page with Search
|
|
|
|
**404.html:**
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Page Not Found</title>
|
|
</head>
|
|
<body>
|
|
<h1>404 - Page Not Found</h1>
|
|
<p>Try searching:</p>
|
|
<form action="/search" method="get">
|
|
<input type="text" name="q" placeholder="Search...">
|
|
<button>Search</button>
|
|
</form>
|
|
<p><a href="/">Return to Homepage</a></p>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
#### Branded Error Pages
|
|
|
|
```apache
|
|
ErrorDocument 400 /errors/400.html
|
|
ErrorDocument 401 /errors/401.html
|
|
ErrorDocument 403 /errors/403.html
|
|
ErrorDocument 404 /errors/404.html
|
|
ErrorDocument 500 /errors/500.html
|
|
ErrorDocument 502 /errors/502.html
|
|
ErrorDocument 503 /errors/503.html
|
|
```
|
|
|
|
Each page styled with your brand colors, logo, navigation.
|
|
|
|
---
|
|
|
|
## 7. FilesMatch Directives
|
|
|
|
### What is FilesMatch?
|
|
|
|
FilesMatch applies directives only to files matching a regex pattern. Perfect for caching strategies, security headers per file type.
|
|
|
|
### Syntax
|
|
|
|
```apache
|
|
<FilesMatch "regex">
|
|
# Directives here apply only to matching files
|
|
Header set Name "Value"
|
|
</FilesMatch>
|
|
```
|
|
|
|
### Common File Patterns
|
|
|
|
| Pattern | Matches |
|
|
|---------|---------|
|
|
| `\.(jpg\|png\|gif)$` | Images |
|
|
| `\.(css\|js)$` | Stylesheets and JavaScript |
|
|
| `\.(woff2?\|ttf\|eot)$` | Fonts |
|
|
| `\.(pdf\|doc\|docx)$` | Documents |
|
|
| `\.(html\|php)$` | Dynamic pages |
|
|
| `\.json$` | JSON files |
|
|
|
|
### How to Use
|
|
|
|
#### Cache Static Assets (Performance Boost)
|
|
|
|
**What it does:** Tells browsers to cache images/fonts for a long time.
|
|
|
|
```apache
|
|
# Images - Cache for 1 year
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
Header unset ETag
|
|
Header unset Last-Modified
|
|
</FilesMatch>
|
|
|
|
# Fonts - Cache for 1 year
|
|
<FilesMatch "\.(woff2?|ttf|eot|otf)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
Header set Access-Control-Allow-Origin "*"
|
|
</FilesMatch>
|
|
|
|
# CSS/JS - Cache for 1 week (you update these more often)
|
|
<FilesMatch "\.(css|js)$">
|
|
Header set Cache-Control "max-age=604800, public"
|
|
</FilesMatch>
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com/logo.png | grep Cache-Control
|
|
# Should show: Cache-Control: max-age=31536000, public, immutable
|
|
```
|
|
|
|
**Performance Impact:**
|
|
- First visit: Downloads all files
|
|
- Return visits: Loads from browser cache (instant!)
|
|
- Page load time: -50% to -80%
|
|
|
|
#### Security Headers for HTML/PHP
|
|
|
|
**What it does:** Applies security headers only to pages (not images).
|
|
|
|
```apache
|
|
<FilesMatch "\.(html|htm|php)$">
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set X-XSS-Protection "1; mode=block"
|
|
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
|
</FilesMatch>
|
|
```
|
|
|
|
#### Prevent Caching of Dynamic Content
|
|
|
|
**What it does:** Ensures dynamic pages are never cached.
|
|
|
|
```apache
|
|
<FilesMatch "\.(html|php|json|xml)$">
|
|
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
Header set Pragma "no-cache"
|
|
Header set Expires "0"
|
|
</FilesMatch>
|
|
```
|
|
|
|
#### CORS for Fonts (Fix Font Loading)
|
|
|
|
**What it does:** Allows fonts to load from CDN or different domain.
|
|
|
|
```apache
|
|
<FilesMatch "\.(woff2?|ttf|eot|otf)$">
|
|
Header set Access-Control-Allow-Origin "*"
|
|
</FilesMatch>
|
|
```
|
|
|
|
**Use case:** Fixes "Font from origin has been blocked by CORS policy" errors.
|
|
|
|
#### Download Headers for Files
|
|
|
|
**What it does:** Forces download instead of displaying in browser.
|
|
|
|
```apache
|
|
<FilesMatch "\.(pdf|zip|tar|gz|doc|docx|xls|xlsx)$">
|
|
Header set Content-Disposition "attachment"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
</FilesMatch>
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### WordPress Performance Optimization
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Cache WordPress static assets
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
</FilesMatch>
|
|
|
|
# Cache CSS/JS (with version strings in WordPress)
|
|
<FilesMatch "\.(css|js)$">
|
|
Header set Cache-Control "max-age=2592000, public"
|
|
</FilesMatch>
|
|
|
|
# Don't cache WordPress admin
|
|
<FilesMatch "(wp-login|wp-admin|wp-cron)\.php$">
|
|
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
</FilesMatch>
|
|
```
|
|
|
|
**Result:** PageSpeed score +20-30 points
|
|
|
|
#### WooCommerce Security
|
|
|
|
```apache
|
|
# Protect sensitive files
|
|
<FilesMatch "(\.log|\.sql|\.md|readme\.txt|license\.txt)$">
|
|
Order deny,allow
|
|
Deny from all
|
|
</FilesMatch>
|
|
|
|
# JSON API security
|
|
<FilesMatch "\.json$">
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set Content-Type "application/json; charset=utf-8"
|
|
</FilesMatch>
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Complete Caching Strategy
|
|
|
|
```apache
|
|
# Aggressive caching for static assets (1 year)
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico|woff2?|ttf|eot|otf)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
Header unset ETag
|
|
</FilesMatch>
|
|
|
|
# Moderate caching for CSS/JS (1 month)
|
|
<FilesMatch "\.(css|js)$">
|
|
Header set Cache-Control "max-age=2592000, public"
|
|
</FilesMatch>
|
|
|
|
# Short caching for HTML (1 hour)
|
|
<FilesMatch "\.html$">
|
|
Header set Cache-Control "max-age=3600, public"
|
|
</FilesMatch>
|
|
|
|
# No caching for dynamic content
|
|
<FilesMatch "\.(php|json)$">
|
|
Header set Cache-Control "no-cache, must-revalidate"
|
|
</FilesMatch>
|
|
```
|
|
|
|
#### Media Library Protection
|
|
|
|
```apache
|
|
# Prevent hotlinking (bandwidth theft)
|
|
<FilesMatch "\.(jpg|jpeg|png|gif)$">
|
|
SetEnvIf Referer "^https://yourdomain\.com" local_ref
|
|
SetEnvIf Referer "^$" local_ref
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from env=local_ref
|
|
</FilesMatch>
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Expires Directives
|
|
|
|
### What is mod_expires?
|
|
|
|
Alternative syntax for setting cache expiration. More concise than Cache-Control headers.
|
|
|
|
### Directives
|
|
|
|
```apache
|
|
ExpiresActive On
|
|
ExpiresByType mime-type base+seconds
|
|
```
|
|
|
|
### Time Bases
|
|
|
|
- **A** = Access time (when user requests file)
|
|
- **M** = Modification time (when file was last modified)
|
|
|
|
### Common Durations
|
|
|
|
| Duration | Seconds | Example |
|
|
|----------|---------|---------|
|
|
| 1 minute | 60 | `A60` |
|
|
| 1 hour | 3600 | `A3600` |
|
|
| 1 day | 86400 | `A86400` |
|
|
| 1 week | 604800 | `A604800` |
|
|
| 1 month | 2592000 | `A2592000` |
|
|
| 1 year | 31557600 | `A31557600` |
|
|
|
|
### How to Use
|
|
|
|
#### Complete Expiration Strategy
|
|
|
|
```apache
|
|
# Enable module
|
|
ExpiresActive On
|
|
|
|
# Images - 1 year
|
|
ExpiresByType image/jpeg A31557600
|
|
ExpiresByType image/png A31557600
|
|
ExpiresByType image/gif A31557600
|
|
ExpiresByType image/webp A31557600
|
|
ExpiresByType image/svg+xml A31557600
|
|
ExpiresByType image/x-icon A31557600
|
|
|
|
# CSS and JavaScript - 1 month
|
|
ExpiresByType text/css A2592000
|
|
ExpiresByType application/javascript A2592000
|
|
ExpiresByType application/x-javascript A2592000
|
|
ExpiresByType text/javascript A2592000
|
|
|
|
# Fonts - 1 year
|
|
ExpiresByType font/ttf A31557600
|
|
ExpiresByType font/woff A31557600
|
|
ExpiresByType font/woff2 A31557600
|
|
ExpiresByType application/font-woff A31557600
|
|
ExpiresByType application/font-woff2 A31557600
|
|
|
|
# HTML - no cache
|
|
ExpiresByType text/html A0
|
|
|
|
# PDF - 1 month
|
|
ExpiresByType application/pdf A2592000
|
|
|
|
# JSON/XML - 1 hour
|
|
ExpiresByType application/json A3600
|
|
ExpiresByType application/xml A3600
|
|
```
|
|
|
|
**Testing:**
|
|
```bash
|
|
curl -I https://yourdomain.com/image.jpg | grep -E "Expires|Cache-Control"
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### WordPress Caching
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/.htaccess
|
|
|
|
ExpiresActive On
|
|
|
|
# WordPress uploads (images in wp-content/uploads)
|
|
ExpiresByType image/jpeg A31557600
|
|
ExpiresByType image/png A31557600
|
|
ExpiresByType image/gif A31557600
|
|
|
|
# WordPress theme assets
|
|
ExpiresByType text/css A2592000
|
|
ExpiresByType application/javascript A2592000
|
|
|
|
# WordPress HTML (dynamic, don't cache)
|
|
ExpiresByType text/html A0
|
|
```
|
|
|
|
### FilesMatch vs Expires
|
|
|
|
**Use FilesMatch when:**
|
|
- Need multiple headers per file type
|
|
- Need complex regex patterns
|
|
- Want more control
|
|
|
|
**Use Expires when:**
|
|
- Only setting cache expiration
|
|
- Want concise syntax
|
|
- Working with MIME types
|
|
|
|
**Both together:**
|
|
```apache
|
|
ExpiresActive On
|
|
|
|
<FilesMatch "\.(jpg|png|gif)$">
|
|
ExpiresByType image/jpeg A31557600
|
|
Header set Cache-Control "public, immutable"
|
|
Header unset ETag
|
|
</FilesMatch>
|
|
```
|
|
|
|
---
|
|
|
|
## 9. PHP Directives
|
|
|
|
### What Are PHP Directives?
|
|
|
|
Change PHP configuration per-directory without editing php.ini.
|
|
|
|
### Directives
|
|
|
|
| Directive | Syntax | Purpose |
|
|
|-----------|--------|---------|
|
|
| **php_value** | `php_value name value` | Set numeric/string values |
|
|
| **php_flag** | `php_flag name on/off` | Set boolean (on/off) values |
|
|
|
|
### Requirements
|
|
|
|
- Must use **LSPHP** (not PHP-FPM)
|
|
- Must be **PHP_INI_ALL** or **PHP_INI_PERDIR** directive
|
|
- CyberPanel uses LSPHP by default ✅
|
|
|
|
### How to Use
|
|
|
|
#### Memory and Execution Limits
|
|
|
|
**What it does:** Allows scripts to use more memory/time.
|
|
|
|
```apache
|
|
# Increase memory (default 128M)
|
|
php_value memory_limit 256M
|
|
|
|
# Increase execution time (default 30s)
|
|
php_value max_execution_time 300
|
|
|
|
# Increase input time (default 60s)
|
|
php_value max_input_time 300
|
|
|
|
# Increase max input variables (default 1000)
|
|
php_value max_input_vars 5000
|
|
```
|
|
|
|
**Use case:** WordPress imports, WooCommerce bulk operations, data processing.
|
|
|
|
**Testing:**
|
|
```php
|
|
<?php
|
|
echo 'Memory Limit: ' . ini_get('memory_limit') . "\n";
|
|
echo 'Max Execution Time: ' . ini_get('max_execution_time') . "\n";
|
|
?>
|
|
```
|
|
|
|
#### Upload Limits
|
|
|
|
**What it does:** Allows larger file uploads.
|
|
|
|
```apache
|
|
# Allow 100MB uploads (default 2M)
|
|
php_value upload_max_filesize 100M
|
|
php_value post_max_size 100M
|
|
|
|
# Increase max file uploads (default 20)
|
|
php_value max_file_uploads 50
|
|
```
|
|
|
|
**Use case:** Media uploads, plugin/theme installation, backup uploads.
|
|
|
|
**Testing:**
|
|
```php
|
|
<?php
|
|
phpinfo();
|
|
// Search for upload_max_filesize and post_max_size
|
|
?>
|
|
```
|
|
|
|
#### Error Handling
|
|
|
|
**What it does:** Controls error display and logging.
|
|
|
|
```apache
|
|
# Production (hide errors)
|
|
php_flag display_errors off
|
|
php_flag log_errors on
|
|
php_value error_log /home/yourdomain.com/logs/php_errors.log
|
|
|
|
# Development (show errors)
|
|
php_flag display_errors on
|
|
php_value error_reporting 32767
|
|
```
|
|
|
|
**Use case:** Debugging vs production security.
|
|
|
|
#### Session Configuration
|
|
|
|
**What it does:** Configures PHP sessions.
|
|
|
|
```apache
|
|
# Session lifetime (1 hour)
|
|
php_value session.gc_maxlifetime 3600
|
|
|
|
# Session cookie (close browser = logout)
|
|
php_value session.cookie_lifetime 0
|
|
|
|
# Session security
|
|
php_flag session.cookie_httponly on
|
|
php_flag session.cookie_secure on
|
|
php_value session.cookie_samesite Strict
|
|
```
|
|
|
|
**Use case:** Login session duration, security.
|
|
|
|
#### Timezone
|
|
|
|
**What it does:** Sets server timezone.
|
|
|
|
```apache
|
|
php_value date.timezone "America/New_York"
|
|
php_value date.timezone "Europe/London"
|
|
php_value date.timezone "Asia/Tokyo"
|
|
```
|
|
|
|
**Use case:** Correct timestamps in logs, posts, events.
|
|
|
|
**Testing:**
|
|
```php
|
|
<?php
|
|
echo date_default_timezone_get();
|
|
?>
|
|
```
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### WordPress Performance Tuning
|
|
|
|
```apache
|
|
# In /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# WordPress recommended settings
|
|
php_value memory_limit 256M
|
|
php_value max_execution_time 300
|
|
php_value max_input_time 300
|
|
php_value max_input_vars 5000
|
|
php_value upload_max_filesize 64M
|
|
php_value post_max_size 64M
|
|
|
|
# Production error handling
|
|
php_flag display_errors off
|
|
php_flag log_errors on
|
|
php_value error_log /home/yourdomain.com/logs/php_errors.log
|
|
|
|
# Session security
|
|
php_flag session.cookie_httponly on
|
|
php_flag session.cookie_secure on
|
|
```
|
|
|
|
#### WooCommerce Optimization
|
|
|
|
```apache
|
|
# WooCommerce needs more resources
|
|
php_value memory_limit 512M
|
|
php_value max_execution_time 600
|
|
php_value max_input_vars 10000
|
|
php_value upload_max_filesize 128M
|
|
php_value post_max_size 128M
|
|
```
|
|
|
|
#### Development vs Production
|
|
|
|
**Development .htaccess:**
|
|
```apache
|
|
php_flag display_errors on
|
|
php_value error_reporting 32767
|
|
php_flag display_startup_errors on
|
|
php_value memory_limit 512M
|
|
```
|
|
|
|
**Production .htaccess:**
|
|
```apache
|
|
php_flag display_errors off
|
|
php_flag log_errors on
|
|
php_value error_log /home/yourdomain.com/logs/php_errors.log
|
|
php_value memory_limit 256M
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Fix "Memory Exhausted" Error
|
|
|
|
```apache
|
|
php_value memory_limit 512M
|
|
```
|
|
|
|
#### Fix "Maximum Execution Time Exceeded"
|
|
|
|
```apache
|
|
php_value max_execution_time 300
|
|
```
|
|
|
|
#### Fix "Upload Failed" (File Too Large)
|
|
|
|
```apache
|
|
php_value upload_max_filesize 100M
|
|
php_value post_max_size 100M
|
|
```
|
|
|
|
#### Fix "Maximum Input Vars Exceeded" (WordPress Theme Options)
|
|
|
|
```apache
|
|
php_value max_input_vars 10000
|
|
```
|
|
|
|
### Supported Directives
|
|
|
|
Most PHP ini settings can be changed:
|
|
|
|
**✅ Supported:**
|
|
- memory_limit
|
|
- max_execution_time
|
|
- max_input_time
|
|
- max_input_vars
|
|
- upload_max_filesize
|
|
- post_max_size
|
|
- display_errors
|
|
- log_errors
|
|
- error_log
|
|
- error_reporting
|
|
- session.* (all session directives)
|
|
- date.timezone
|
|
- default_charset
|
|
- output_buffering
|
|
|
|
**❌ Not Supported:**
|
|
- enable_dl (PHP_INI_SYSTEM only)
|
|
- safe_mode (deprecated)
|
|
- open_basedir (security setting)
|
|
|
|
---
|
|
|
|
## 10. Brute Force Protection
|
|
|
|
### What is Brute Force Protection?
|
|
|
|
Built-in WordPress login protection. Limits POST requests to wp-login.php and xmlrpc.php to stop password guessing attacks.
|
|
|
|
### Quick Start
|
|
|
|
```apache
|
|
BruteForceProtection On
|
|
```
|
|
|
|
That's it! Default settings: 10 attempts per 5 minutes.
|
|
|
|
### How It Works
|
|
|
|
1. Tracks POST requests to `/wp-login.php` and `/xmlrpc.php`
|
|
2. Counts requests per IP address
|
|
3. Uses time-window quota system (e.g., 10 requests per 300 seconds)
|
|
4. When quota exhausted, applies action (block, log, or throttle)
|
|
5. Quota resets after time window expires
|
|
|
|
### Phase 1 Directives (Basic)
|
|
|
|
| Directive | Values | Default | Description |
|
|
|-----------|--------|---------|-------------|
|
|
| **BruteForceProtection** | On/Off | Off | Enable protection |
|
|
| **BruteForceAllowedAttempts** | 1-1000 | 10 | Max POST requests per window |
|
|
| **BruteForceWindow** | 60-86400 | 300 | Time window (seconds) |
|
|
| **BruteForceAction** | block/log/throttle | block | Action when limit exceeded |
|
|
|
|
### Phase 2 Directives (Advanced)
|
|
|
|
| Directive | Values | Default | Description |
|
|
|-----------|--------|---------|-------------|
|
|
| **BruteForceXForwardedFor** | On/Off | Off | Use X-Forwarded-For for real IP |
|
|
| **BruteForceWhitelist** | IP list | (empty) | Bypass protection for these IPs |
|
|
| **BruteForceProtectPath** | path | (none) | Additional paths to protect |
|
|
|
|
### Actions Explained
|
|
|
|
#### block (Recommended)
|
|
|
|
**What it does:** Immediately returns 403 Forbidden.
|
|
|
|
```apache
|
|
BruteForceAction block
|
|
```
|
|
|
|
**Response:**
|
|
```
|
|
HTTP/1.1 403 Forbidden
|
|
Content-Type: text/html
|
|
|
|
<html>
|
|
<head><title>403 Forbidden</title></head>
|
|
<body>
|
|
<h1>Access Denied</h1>
|
|
<p>Too many login attempts. Please try again later.</p>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
**Use case:** Production sites, maximum security.
|
|
|
|
#### log (Monitoring)
|
|
|
|
**What it does:** Allows request but logs to error.log.
|
|
|
|
```apache
|
|
BruteForceAction log
|
|
```
|
|
|
|
**Use case:** Testing, monitoring before enabling blocking.
|
|
|
|
**Check logs:**
|
|
```bash
|
|
grep BruteForce /usr/local/lsws/logs/error.log
|
|
```
|
|
|
|
#### throttle (New in v2.2.0)
|
|
|
|
**What it does:** Applies progressive delays before responding.
|
|
|
|
```apache
|
|
BruteForceAction throttle
|
|
```
|
|
|
|
**Throttle levels:**
|
|
|
|
| Over-Limit Attempts | Level | Delay | HTTP Response |
|
|
|---------------------|-------|-------|---------------|
|
|
| 1-2 | Soft | 2 seconds | 429 Too Many Requests |
|
|
| 3-5 | Medium | 5 seconds | 429 Too Many Requests |
|
|
| 6+ | Hard | 15 seconds | 429 Too Many Requests |
|
|
|
|
**Response includes:**
|
|
```
|
|
HTTP/1.1 429 Too Many Requests
|
|
Retry-After: 15
|
|
```
|
|
|
|
**Use case:** Slows down attackers while allowing legitimate users who forgot password.
|
|
|
|
### How to Use
|
|
|
|
#### Basic Protection (Small Site)
|
|
|
|
```apache
|
|
# Simple protection
|
|
BruteForceProtection On
|
|
```
|
|
|
|
**Result:** Default 10 attempts per 5 minutes, then block.
|
|
|
|
#### Strict Protection (High Security)
|
|
|
|
```apache
|
|
# Only 3 attempts per 15 minutes
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 3
|
|
BruteForceWindow 900
|
|
BruteForceAction block
|
|
```
|
|
|
|
**Result:** Very strict, good for high-value targets.
|
|
|
|
#### Moderate Protection with Throttle (Recommended)
|
|
|
|
```apache
|
|
# 5 attempts per 5 minutes, then progressive throttle
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
```
|
|
|
|
**Result:** Legitimate users can still login (slowly), attackers waste time.
|
|
|
|
#### Behind Cloudflare/Proxy
|
|
|
|
**Problem:** All requests appear to come from proxy IP.
|
|
|
|
**Solution:** Use X-Forwarded-For to get real client IP.
|
|
|
|
```apache
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
BruteForceXForwardedFor On
|
|
```
|
|
|
|
**Important:** Only enable if behind trusted proxy (Cloudflare, nginx).
|
|
|
|
#### With IP Whitelist
|
|
|
|
**What it does:** Allows unlimited attempts from trusted IPs.
|
|
|
|
```apache
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 3
|
|
BruteForceWindow 900
|
|
BruteForceAction block
|
|
BruteForceWhitelist 203.0.113.50, 192.168.1.0/24, 10.0.0.0/8
|
|
```
|
|
|
|
**Use case:** Whitelist office IP, admin home IP, VPN range.
|
|
|
|
#### Protect Custom Login Pages
|
|
|
|
```apache
|
|
# Protect custom endpoints
|
|
BruteForceProtection On
|
|
BruteForceProtectPath /admin/login
|
|
BruteForceProtectPath /api/auth
|
|
BruteForceProtectPath /members/signin
|
|
```
|
|
|
|
**Default protected:** `/wp-login.php` and `/xmlrpc.php`
|
|
|
|
### CyberPanel Integration
|
|
|
|
#### WordPress Security Setup
|
|
|
|
**Step 1:** Navigate to website .htaccess
|
|
```bash
|
|
cd /home/yourdomain.com/public_html
|
|
nano .htaccess
|
|
```
|
|
|
|
**Step 2:** Add protection
|
|
```apache
|
|
# At top of .htaccess
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
```
|
|
|
|
**Step 3:** Save and test
|
|
```bash
|
|
# Try multiple wrong passwords
|
|
# After 5 attempts, should get throttled
|
|
```
|
|
|
|
**Step 4:** Monitor logs
|
|
```bash
|
|
tail -f /usr/local/lsws/logs/error.log | grep BruteForce
|
|
```
|
|
|
|
#### WooCommerce + WordPress
|
|
|
|
```apache
|
|
# Protect both WordPress and WooCommerce login
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction block
|
|
BruteForceProtectPath /my-account/
|
|
BruteForceProtectPath /checkout/
|
|
```
|
|
|
|
#### Multi-Site WordPress
|
|
|
|
```apache
|
|
# Apply to all subsites
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
BruteForceXForwardedFor On
|
|
```
|
|
|
|
### Shared Memory Storage
|
|
|
|
**Location:** `/dev/shm/ols/`
|
|
|
|
```bash
|
|
ls -la /dev/shm/ols/
|
|
# BFProt.shm - Stores IP quota data
|
|
# BFProt.lock - Synchronization lock
|
|
```
|
|
|
|
**Persistence:** Data survives OpenLiteSpeed restarts (stored in tmpfs).
|
|
|
|
**Reset/Clear:**
|
|
```bash
|
|
# Clear all quota data
|
|
rm -f /dev/shm/ols/BFProt.*
|
|
/usr/local/lsws/bin/lswsctrl restart
|
|
```
|
|
|
|
**Use case:** Accidentally locked out, need to reset.
|
|
|
|
### Monitoring and Logs
|
|
|
|
#### View Brute Force Events
|
|
|
|
```bash
|
|
grep BruteForce /usr/local/lsws/logs/error.log
|
|
```
|
|
|
|
**Sample log entries:**
|
|
|
|
```
|
|
[INFO] [BruteForce] Initialized: 10 attempts per 300s window, action: throttle
|
|
[WARN] [BruteForce] Warning: 192.168.1.50 has 2 attempts remaining for /wp-login.php
|
|
[NOTICE] [BruteForce] Blocked 192.168.1.50 - quota exhausted for /wp-login.php (10 attempts in 300s)
|
|
[NOTICE] [BruteForce] Throttling 192.168.1.50 (medium level, 5000ms delay) for /wp-login.php
|
|
```
|
|
|
|
#### Real-Time Monitoring
|
|
|
|
```bash
|
|
# Watch in real-time
|
|
tail -f /usr/local/lsws/logs/error.log | grep BruteForce
|
|
|
|
# Count blocked IPs today
|
|
grep "BruteForce.*Blocked" /usr/local/lsws/logs/error.log | grep "$(date +%Y-%m-%d)" | wc -l
|
|
```
|
|
|
|
#### Check Specific IP
|
|
|
|
```bash
|
|
grep "BruteForce.*192.168.1.50" /usr/local/lsws/logs/error.log
|
|
```
|
|
|
|
### Testing Brute Force Protection
|
|
|
|
#### Manual Test
|
|
|
|
```bash
|
|
# Try multiple wrong passwords
|
|
for i in {1..15}; do
|
|
curl -X POST https://yourdomain.com/wp-login.php \
|
|
-d "log=admin&pwd=wrong$i&wp-submit=Log+In" \
|
|
-I | grep "HTTP"
|
|
sleep 1
|
|
done
|
|
|
|
# After BruteForceAllowedAttempts, should see:
|
|
# HTTP/1.1 403 Forbidden (if action=block)
|
|
# HTTP/1.1 429 Too Many Requests (if action=throttle)
|
|
```
|
|
|
|
#### Check Logs
|
|
|
|
```bash
|
|
grep BruteForce /usr/local/lsws/logs/error.log | tail -20
|
|
```
|
|
|
|
### Common Use Cases
|
|
|
|
#### Production WordPress
|
|
|
|
```apache
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction block
|
|
```
|
|
|
|
#### Behind Cloudflare
|
|
|
|
```apache
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
BruteForceXForwardedFor On
|
|
```
|
|
|
|
#### Enterprise with Whitelist
|
|
|
|
```apache
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 3
|
|
BruteForceWindow 900
|
|
BruteForceAction block
|
|
BruteForceXForwardedFor On
|
|
BruteForceWhitelist 10.0.0.0/8, 192.168.1.0/24, 203.0.113.100
|
|
BruteForceProtectPath /admin/
|
|
BruteForceProtectPath /api/login
|
|
```
|
|
|
|
### Troubleshooting
|
|
|
|
**Problem:** Legitimate users getting blocked
|
|
|
|
**Solution:**
|
|
```apache
|
|
# Increase allowed attempts
|
|
BruteForceAllowedAttempts 10
|
|
|
|
# Or use throttle instead of block
|
|
BruteForceAction throttle
|
|
|
|
# Or whitelist their IP
|
|
BruteForceWhitelist 203.0.113.50
|
|
```
|
|
|
|
**Problem:** Protection not working
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Check module loaded
|
|
ls -la /usr/local/lsws/modules/cyberpanel_ols.so
|
|
|
|
# Check .htaccess syntax
|
|
cat /home/yourdomain.com/public_html/.htaccess | grep BruteForce
|
|
|
|
# Check logs
|
|
grep BruteForce /usr/local/lsws/logs/error.log
|
|
|
|
# Restart OpenLiteSpeed
|
|
/usr/local/lsws/bin/lswsctrl restart
|
|
```
|
|
|
|
**Problem:** Shared memory errors
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Create directory if missing
|
|
mkdir -p /dev/shm/ols
|
|
|
|
# Set permissions
|
|
chmod 755 /dev/shm/ols
|
|
|
|
# Restart
|
|
/usr/local/lsws/bin/lswsctrl restart
|
|
```
|
|
|
|
---
|
|
|
|
## CyberPanel Integration
|
|
|
|
### Accessing Website Files
|
|
|
|
#### Via CyberPanel File Manager
|
|
|
|
1. Log into **CyberPanel** (https://yourserver:8090)
|
|
2. Click **File Manager**
|
|
3. Navigate to `/home/yourdomain.com/public_html`
|
|
4. Create or edit `.htaccess`
|
|
5. Add directives from this guide
|
|
6. Click **Save**
|
|
|
|
#### Via SSH
|
|
|
|
```bash
|
|
# Log in via SSH
|
|
ssh root@yourserver
|
|
|
|
# Navigate to website
|
|
cd /home/yourdomain.com/public_html
|
|
|
|
# Edit .htaccess
|
|
nano .htaccess
|
|
|
|
# Add directives
|
|
# Save: Ctrl+X, Y, Enter
|
|
```
|
|
|
|
#### Via FTP (FileZilla)
|
|
|
|
1. Connect via FTP
|
|
2. Navigate to `/home/yourdomain.com/public_html`
|
|
3. Download `.htaccess`
|
|
4. Edit locally
|
|
5. Upload back
|
|
|
|
### Creating New Website
|
|
|
|
1. **Create Website** in CyberPanel
|
|
2. **Navigate to directory:**
|
|
```bash
|
|
cd /home/newsite.com/public_html
|
|
```
|
|
3. **Create .htaccess:**
|
|
```bash
|
|
nano .htaccess
|
|
```
|
|
4. **Add base configuration:**
|
|
```apache
|
|
# Security headers
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
|
|
# Brute force protection
|
|
BruteForceProtection On
|
|
|
|
# Cache static assets
|
|
<FilesMatch "\.(jpg|png|gif|css|js)$">
|
|
Header set Cache-Control "max-age=31536000, public"
|
|
</FilesMatch>
|
|
```
|
|
|
|
### WordPress on CyberPanel
|
|
|
|
#### Complete WordPress .htaccess
|
|
|
|
```apache
|
|
# Security Headers
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set X-XSS-Protection "1; mode=block"
|
|
Header unset X-Powered-By
|
|
|
|
# Brute Force Protection
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
|
|
# Performance - Cache Static Assets
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
</FilesMatch>
|
|
|
|
<FilesMatch "\.(css|js)$">
|
|
Header set Cache-Control "max-age=2592000, public"
|
|
</FilesMatch>
|
|
|
|
# PHP Configuration
|
|
php_value memory_limit 256M
|
|
php_value upload_max_filesize 64M
|
|
php_value post_max_size 64M
|
|
php_value max_execution_time 300
|
|
php_flag display_errors off
|
|
|
|
# WordPress Rewrite Rules (leave as-is)
|
|
# BEGIN WordPress
|
|
<IfModule mod_rewrite.c>
|
|
RewriteEngine On
|
|
RewriteBase /
|
|
RewriteRule ^index\.php$ - [L]
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteRule . /index.php [L]
|
|
</IfModule>
|
|
# END WordPress
|
|
```
|
|
|
|
### Staging Environment
|
|
|
|
```apache
|
|
# Staging site - restrict access
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from YOUR.OFFICE.IP
|
|
Allow from YOUR.HOME.IP
|
|
|
|
# No search engine indexing
|
|
Header set X-Robots-Tag "noindex, nofollow"
|
|
|
|
# Show errors (development)
|
|
php_flag display_errors on
|
|
php_value error_reporting 32767
|
|
```
|
|
|
|
### Testing After Configuration
|
|
|
|
```bash
|
|
# Test headers
|
|
curl -I https://yourdomain.com | grep -E "X-Frame|Cache-Control|X-Content"
|
|
|
|
# Test specific file
|
|
curl -I https://yourdomain.com/wp-content/uploads/2024/12/image.jpg | grep Cache
|
|
|
|
# Test PHP settings
|
|
echo '<?php phpinfo(); ?>' > /home/yourdomain.com/public_html/info.php
|
|
curl https://yourdomain.com/info.php | grep memory_limit
|
|
|
|
# Clean up
|
|
rm /home/yourdomain.com/public_html/info.php
|
|
```
|
|
|
|
---
|
|
|
|
## Real-World Examples
|
|
|
|
### Example 1: High-Performance WordPress
|
|
|
|
```apache
|
|
# Security
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set X-XSS-Protection "1; mode=block"
|
|
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
|
Header unset Server
|
|
Header unset X-Powered-By
|
|
|
|
# Brute Force Protection
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
BruteForceXForwardedFor On
|
|
|
|
# Aggressive Caching
|
|
<FilesMatch "\.(jpg|jpeg|png|gif|webp|svg|ico)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
Header unset ETag
|
|
</FilesMatch>
|
|
|
|
<FilesMatch "\.(woff2?|ttf|eot)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
Header set Access-Control-Allow-Origin "*"
|
|
</FilesMatch>
|
|
|
|
<FilesMatch "\.(css|js)$">
|
|
Header set Cache-Control "max-age=2592000, public"
|
|
</FilesMatch>
|
|
|
|
# PHP Optimization
|
|
php_value memory_limit 256M
|
|
php_value max_execution_time 300
|
|
php_value upload_max_filesize 64M
|
|
php_value post_max_size 64M
|
|
php_flag display_errors off
|
|
php_flag log_errors on
|
|
```
|
|
|
|
### Example 2: WooCommerce E-commerce
|
|
|
|
```apache
|
|
# Security Headers
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set X-XSS-Protection "1; mode=block"
|
|
|
|
# Strict Brute Force Protection
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 3
|
|
BruteForceWindow 900
|
|
BruteForceAction block
|
|
BruteForceProtectPath /my-account/
|
|
BruteForceProtectPath /checkout/
|
|
|
|
# Product Image Caching
|
|
<FilesMatch "\.(jpg|jpeg|png|webp)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
</FilesMatch>
|
|
|
|
# Don't Cache Checkout/Cart
|
|
<FilesMatch "(cart|checkout|my-account)">
|
|
Header set Cache-Control "no-cache, no-store, must-revalidate"
|
|
</FilesMatch>
|
|
|
|
# PHP for WooCommerce
|
|
php_value memory_limit 512M
|
|
php_value max_execution_time 600
|
|
php_value max_input_vars 10000
|
|
php_value upload_max_filesize 128M
|
|
php_value post_max_size 128M
|
|
```
|
|
|
|
### Example 3: API Server
|
|
|
|
```apache
|
|
# CORS for API
|
|
Header set Access-Control-Allow-Origin "*"
|
|
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
|
Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key"
|
|
Header set Access-Control-Max-Age "86400"
|
|
|
|
# JSON Response Headers
|
|
<FilesMatch "\.json$">
|
|
Header set Content-Type "application/json; charset=utf-8"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set Cache-Control "no-cache, must-revalidate"
|
|
</FilesMatch>
|
|
|
|
# API Rate Limiting
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 100
|
|
BruteForceWindow 60
|
|
BruteForceAction throttle
|
|
BruteForceProtectPath /api/
|
|
|
|
# Environment
|
|
SetEnv API_VERSION v2
|
|
SetEnv API_ENVIRONMENT production
|
|
```
|
|
|
|
### Example 4: Static Site with CDN
|
|
|
|
```apache
|
|
# Aggressive Caching
|
|
ExpiresActive On
|
|
ExpiresByType image/jpeg A31557600
|
|
ExpiresByType image/png A31557600
|
|
ExpiresByType image/gif A31557600
|
|
ExpiresByType text/css A31557600
|
|
ExpiresByType application/javascript A31557600
|
|
ExpiresByType text/html A3600
|
|
|
|
# CORS for CDN
|
|
Header set Access-Control-Allow-Origin "*"
|
|
|
|
# Security Headers
|
|
Header set X-Frame-Options "SAMEORIGIN"
|
|
Header set X-Content-Type-Options "nosniff"
|
|
Header set Content-Security-Policy "default-src 'self' https://cdn.example.com"
|
|
|
|
# Remove Server Info
|
|
Header unset Server
|
|
Header unset X-Powered-By
|
|
```
|
|
|
|
### Example 5: Multi-Environment Setup
|
|
|
|
**Production (.htaccess):**
|
|
```apache
|
|
SetEnv APPLICATION_ENV production
|
|
php_flag display_errors off
|
|
php_flag log_errors on
|
|
BruteForceProtection On
|
|
BruteForceAction block
|
|
Header set X-Robots-Tag "index, follow"
|
|
```
|
|
|
|
**Staging (staging.example.com/.htaccess):**
|
|
```apache
|
|
SetEnv APPLICATION_ENV staging
|
|
php_flag display_errors on
|
|
BruteForceProtection On
|
|
BruteForceAction log
|
|
Header set X-Robots-Tag "noindex, nofollow"
|
|
|
|
# IP Restriction
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 203.0.113.50
|
|
```
|
|
|
|
**Development (dev.example.com/.htaccess):**
|
|
```apache
|
|
SetEnv APPLICATION_ENV development
|
|
php_flag display_errors on
|
|
php_value error_reporting 32767
|
|
BruteForceProtection Off
|
|
Header set X-Robots-Tag "noindex, nofollow"
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### 1. Directives Not Working
|
|
|
|
**Symptoms:** Headers not appearing, PHP settings not applied.
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Check module is installed
|
|
ls -la /usr/local/lsws/modules/cyberpanel_ols.so
|
|
# Should show 147KB file
|
|
|
|
# Check module is loaded in config
|
|
grep cyberpanel_ols /usr/local/lsws/conf/httpd_config.conf
|
|
# Should show: module cyberpanel_ols {
|
|
|
|
# Restart OpenLiteSpeed
|
|
/usr/local/lsws/bin/lswsctrl restart
|
|
|
|
# Check logs for errors
|
|
tail -50 /usr/local/lsws/logs/error.log
|
|
```
|
|
|
|
#### 2. .htaccess File Permissions
|
|
|
|
**Symptoms:** 500 Internal Server Error
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Set correct permissions
|
|
chmod 644 /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Set correct ownership
|
|
chown nobody:nogroup /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Verify
|
|
ls -la /home/yourdomain.com/public_html/.htaccess
|
|
# Should show: -rw-r--r-- nobody nogroup
|
|
```
|
|
|
|
#### 3. Headers Not Showing
|
|
|
|
**Symptoms:** `curl -I` doesn't show custom headers
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Clear browser cache
|
|
# Some headers are cached aggressively
|
|
|
|
# Test with curl (bypasses cache)
|
|
curl -I https://yourdomain.com
|
|
|
|
# Test specific file
|
|
curl -I https://yourdomain.com/test.jpg
|
|
|
|
# Check if file exists
|
|
ls -la /home/yourdomain.com/public_html/test.jpg
|
|
|
|
# Verify .htaccess syntax
|
|
cat /home/yourdomain.com/public_html/.htaccess
|
|
```
|
|
|
|
#### 4. PHP Directives Not Applied
|
|
|
|
**Symptoms:** `phpinfo()` shows old values
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Verify using LSPHP (not PHP-FPM)
|
|
# CyberPanel uses LSPHP by default
|
|
|
|
# Check if directive is allowed
|
|
# Some directives are PHP_INI_SYSTEM only
|
|
|
|
# Create test file
|
|
echo '<?php phpinfo(); ?>' > /home/yourdomain.com/public_html/info.php
|
|
|
|
# Check value
|
|
curl https://yourdomain.com/info.php | grep memory_limit
|
|
|
|
# Delete test file
|
|
rm /home/yourdomain.com/public_html/info.php
|
|
```
|
|
|
|
#### 5. Brute Force Protection Not Triggering
|
|
|
|
**Symptoms:** Can submit unlimited login attempts
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Check shared memory directory
|
|
ls -la /dev/shm/ols/
|
|
# Should show BFProt.shm and BFProt.lock
|
|
|
|
# Create if missing
|
|
mkdir -p /dev/shm/ols
|
|
chmod 755 /dev/shm/ols
|
|
|
|
# Check .htaccess syntax
|
|
grep BruteForce /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Must be POST request to protected path
|
|
curl -X POST https://yourdomain.com/wp-login.php -d "log=test&pwd=test"
|
|
|
|
# Check logs
|
|
grep BruteForce /usr/local/lsws/logs/error.log
|
|
|
|
# Restart
|
|
/usr/local/lsws/bin/lswsctrl restart
|
|
```
|
|
|
|
#### 6. Access Control Allowing All
|
|
|
|
**Symptoms:** IP restrictions not working
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Verify your actual IP
|
|
curl ifconfig.me
|
|
|
|
# Check CIDR syntax
|
|
# 192.168.1.0/24 = 192.168.1.1 to 192.168.1.254
|
|
# 10.0.0.0/8 = 10.0.0.0 to 10.255.255.255
|
|
|
|
# Check logs for access decisions
|
|
grep "cyberpanel_access" /usr/local/lsws/logs/error.log
|
|
|
|
# Test with curl from different IP
|
|
curl -I https://yourdomain.com
|
|
# Should get 403 if not allowed
|
|
```
|
|
|
|
#### 7. Redirect Loop
|
|
|
|
**Symptoms:** ERR_TOO_MANY_REDIRECTS
|
|
|
|
**Solutions:**
|
|
|
|
```bash
|
|
# Check for conflicting redirects
|
|
grep Redirect /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Common mistake:
|
|
# BAD: Both redirects active
|
|
# Redirect 301 / https://example.com/
|
|
# Redirect 301 / https://www.example.com/
|
|
|
|
# GOOD: Only one
|
|
Redirect 301 / https://www.example.com/
|
|
|
|
# Check WordPress settings
|
|
# wp-admin > Settings > General
|
|
# WordPress Address and Site Address must match
|
|
```
|
|
|
|
### Getting Help
|
|
|
|
#### Enable Debug Logging
|
|
|
|
```bash
|
|
# Edit OpenLiteSpeed config
|
|
nano /usr/local/lsws/conf/httpd_config.conf
|
|
|
|
# Change Log Level to DEBUG
|
|
# Restart
|
|
/usr/local/lsws/bin/lswsctrl restart
|
|
|
|
# Monitor logs
|
|
tail -f /usr/local/lsws/logs/error.log
|
|
```
|
|
|
|
#### Collect Information
|
|
|
|
```bash
|
|
# Module version
|
|
ls -lh /usr/local/lsws/modules/cyberpanel_ols.so
|
|
|
|
# OpenLiteSpeed version
|
|
/usr/local/lsws/bin/openlitespeed -v
|
|
|
|
# Check .htaccess
|
|
cat /home/yourdomain.com/public_html/.htaccess
|
|
|
|
# Recent logs
|
|
tail -100 /usr/local/lsws/logs/error.log
|
|
|
|
# Test headers
|
|
curl -I https://yourdomain.com
|
|
```
|
|
|
|
#### Report Issue
|
|
|
|
When reporting issues, include:
|
|
|
|
1. **What you're trying to do** (which feature)
|
|
2. **.htaccess content** (sanitized)
|
|
3. **Expected behavior** vs **actual behavior**
|
|
4. **Error logs** (last 50 lines)
|
|
5. **Test results** (curl output)
|
|
6. **Module version** and **OpenLiteSpeed version**
|
|
|
|
---
|
|
|
|
## Performance Optimization
|
|
|
|
### Best Practices
|
|
|
|
1. **Minimize .htaccess size** - Only include necessary directives
|
|
2. **Use FilesMatch carefully** - Each pattern adds regex overhead
|
|
3. **Prefer block over throttle** - Throttle holds connections longer
|
|
4. **Whitelist known IPs** - Skips brute force checks entirely
|
|
5. **Set long cache times** - Reduce server load
|
|
|
|
### Benchmarks
|
|
|
|
| Metric | Value |
|
|
|--------|-------|
|
|
| Overhead per request | < 1ms |
|
|
| Memory per cached .htaccess | ~2KB |
|
|
| Memory per tracked IP (brute force) | ~64 bytes |
|
|
| Cache invalidation | mtime-based (instant) |
|
|
|
|
### Optimization Examples
|
|
|
|
**Before (Slow):**
|
|
```apache
|
|
# Every request checks all patterns
|
|
Header set X-Custom "Value"
|
|
Header set X-Another "Value"
|
|
Header set X-More "Value"
|
|
|
|
<FilesMatch ".*">
|
|
Header set Cache-Control "max-age=3600"
|
|
</FilesMatch>
|
|
```
|
|
|
|
**After (Fast):**
|
|
```apache
|
|
# Only static assets checked
|
|
<FilesMatch "\.(jpg|png|css|js)$">
|
|
Header set Cache-Control "max-age=31536000, public, immutable"
|
|
</FilesMatch>
|
|
```
|
|
|
|
---
|
|
|
|
## Appendix
|
|
|
|
### Quick Reference
|
|
|
|
#### Headers
|
|
```apache
|
|
Header set Name "Value"
|
|
Header unset Name
|
|
Header append Name "Value"
|
|
```
|
|
|
|
#### Access Control
|
|
```apache
|
|
Order deny,allow
|
|
Deny from all
|
|
Allow from 192.168.1.0/24
|
|
```
|
|
|
|
#### Redirects
|
|
```apache
|
|
Redirect 301 /old /new
|
|
RedirectMatch 301 ^/blog/(.*)$ /news/$1
|
|
```
|
|
|
|
#### PHP
|
|
```apache
|
|
php_value memory_limit 256M
|
|
php_flag display_errors off
|
|
```
|
|
|
|
#### Brute Force
|
|
```apache
|
|
BruteForceProtection On
|
|
BruteForceAllowedAttempts 5
|
|
BruteForceWindow 300
|
|
BruteForceAction throttle
|
|
```
|
|
|
|
### Common MIME Types
|
|
|
|
```
|
|
image/jpeg, image/png, image/gif, image/webp, image/svg+xml
|
|
text/css, text/html, text/javascript, text/plain
|
|
application/javascript, application/json, application/xml, application/pdf
|
|
font/ttf, font/woff, font/woff2
|
|
```
|
|
|
|
### Time Duration Reference
|
|
|
|
```
|
|
1 minute = 60
|
|
5 minutes = 300
|
|
15 minutes = 900
|
|
1 hour = 3600
|
|
1 day = 86400
|
|
1 week = 604800
|
|
1 month = 2592000
|
|
1 year = 31557600
|
|
```
|
|
|
|
### IP CIDR Cheat Sheet
|
|
|
|
```
|
|
/32 = 1 IP (255.255.255.255)
|
|
/24 = 256 IPs (255.255.255.0)
|
|
/16 = 65,536 IPs (255.255.0.0)
|
|
/8 = 16,777,216 IPs (255.0.0.0)
|
|
```
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
- **GitHub:** [github.com/usmannasir/cyberpanel_ols](https://github.com/usmannasir/cyberpanel_ols)
|
|
- **Community:** [community.cyberpanel.net](https://community.cyberpanel.net)
|
|
|
|
---
|
|
|
|
**Document Version:** 1.0
|
|
**Module Version:** 2.2.0
|
|
**Last Updated:** December 28, 2025
|
|
|
|
---
|
|
|
|
*Thank you for using the CyberPanel OpenLiteSpeed Module!*
|