WafWay Documentation
Welcome to WafWay documentation. WafWay is an enterprise-grade Web Application Firewall that protects your applications from SQL injection, XSS, and other OWASP Top 10 threats.
New to WafWay? Start with our Quick Start guide to get up and running in 5 minutes.
Quick Start
Get WafWay running in just a few commands:
1. Download the Binary
# Using curl
curl -sSL https://get.wafway.io | sh
# Or download directly
wget https://github.com/wafway/wafway/releases/latest/download/wafway-linux-amd64
chmod +x wafway-linux-amd64
sudo mv wafway-linux-amd64 /usr/local/bin/wafway
2. Create Configuration
# Create config directory
sudo mkdir -p /etc/wafway
# Create basic config
sudo tee /etc/wafway/wafway.yaml << 'EOF'
edition: community
server:
listen_addr: "0.0.0.0"
http_port: 80
admin_port: 8080
backend:
url: "http://127.0.0.1:3000" # Your application
timeout: 30s
protection:
mode: "medium"
sqli:
enabled: true
xss:
enabled: true
rate_limit:
enabled: true
requests_per_minute: 100
dashboard:
enabled: true
username: "admin"
EOF
3. Start WafWay
# Run directly
sudo wafway --config /etc/wafway/wafway.yaml
# Or as a service
sudo systemctl enable wafway
sudo systemctl start wafway
4. Access Dashboard
Open your browser and navigate to http://your-server:8080 to access the dashboard.
System Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| Operating System | Linux (kernel 3.10+) | Ubuntu 22.04, AlmaLinux 9 |
| CPU | 1 core | 2+ cores |
| RAM | 512 MB | 1 GB+ |
| Disk Space | 100 MB | 1 GB+ (for logs) |
| Network Ports | 80, 443, 8080 | 80, 443, 8080 |
Linux Installation
WafWay supports all major Linux distributions including Ubuntu, Debian, CentOS, RHEL, and AlmaLinux.
Using the Install Script (Recommended)
curl -sSL https://get.wafway.io | sh
This script will:
- Download the latest WafWay binary
- Create necessary directories
- Install the systemd service
- Create a default configuration
Manual Installation
# Create service user
sudo useradd --system --no-create-home --shell /sbin/nologin wafway
# Create directories
sudo mkdir -p /opt/wafway /etc/wafway /var/log/wafway /var/lib/wafway
# Download binary
sudo wget -O /opt/wafway/wafway \
https://github.com/wafway/wafway/releases/latest/download/wafway-linux-amd64
# Set permissions
sudo chmod 755 /opt/wafway/wafway
sudo chown -R wafway:wafway /var/log/wafway /var/lib/wafway
# Create symlink
sudo ln -sf /opt/wafway/wafway /usr/local/bin/wafway
Docker Installation
# Quick start with Docker
docker run -d \
-p 80:80 \
-p 8080:8080 \
-e BACKEND_URL=http://host.docker.internal:3000 \
wafway/waf:latest
# With docker-compose
version: '3.8'
services:
waf:
image: wafway/waf:latest
ports:
- "80:80"
- "8080:8080"
volumes:
- ./wafway.yaml:/etc/wafway/wafway.yaml
- waf-data:/var/lib/wafway
restart: unless-stopped
volumes:
waf-data:
Nginx Integration
WafWay can be deployed in front of Nginx or behind it, depending on your architecture.
Option 1: WAF in Front of Nginx (Recommended)
# Traffic flow: Internet -> WafWay:80 -> Nginx:8080 -> App
# 1. Update Nginx to listen on port 8080
server {
listen 8080; # Changed from 80
server_name example.com;
# Trust WAF headers
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
# 2. Configure WafWay
backend:
url: "http://127.0.0.1:8080" # Points to Nginx
Option 2: Nginx in Front of WAF
# Traffic flow: Internet -> Nginx:80 -> WafWay:8081 -> App
# Nginx config
server {
listen 80;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# WafWay config
server:
listen_addr: "127.0.0.1" # Local only
http_port: 8081
backend:
url: "http://127.0.0.1:3000"
Basic Configuration
The main configuration file is located at /etc/wafway/wafway.yaml.
# Complete configuration example
edition: enterprise
server:
listen_addr: "0.0.0.0"
http_port: 80
https_port: 443
read_timeout: 30s
write_timeout: 30s
backend:
url: "http://127.0.0.1:8000"
timeout: 30s
health_check_path: "/health"
health_check_interval: 30s
protection:
mode: "medium" # paranoid, high, medium, low, detect
dashboard:
enabled: true
port: 8080
username: "admin"
logging:
level: "info"
file: "/var/log/wafway/waf.log"
Protection Modules
WafWay includes multiple protection modules that can be individually enabled and configured:
| Module | Description | Default |
|---|---|---|
sqli |
SQL Injection detection | Enabled |
xss |
Cross-Site Scripting prevention | Enabled |
command_inj |
Command Injection detection | Enabled |
path_traversal |
Path Traversal attacks | Enabled |
ssrf |
Server-Side Request Forgery | Enabled |
xxe |
XML External Entity | Enabled |
bot |
Bot detection and blocking | Enabled |
scanner |
Vulnerability scanner detection | Enabled |
Rate Limiting
Configure rate limiting to prevent abuse:
rate_limit:
enabled: true
requests_per_minute: 100
window_size: 60
ban_duration: 10m
whitelist:
- "10.0.0.0/8" # Internal network
- "192.168.0.0/16" # Private network
Geo Blocking
Block or allow traffic by country:
geo_block:
enabled: true
geoip_path: "/etc/wafway/GeoLite2-Country.mmdb"
blocked_countries:
- "XX" # Unknown
allowed_countries: [] # Empty = allow all except blocked
block_tor: true
block_vpns: false
Accessing Dashboard
The dashboard is available at http://your-server:8080 by default. On first login:
- Enter the username (default:
admin) - Enter your password (must be set up via the hashpass tool)
- The password is securely hashed using bcrypt
Security Note: For production, restrict dashboard access to trusted IPs or use SSH tunneling.
Password Setup
WafWay uses industry-standard bcrypt password hashing. Use the included hashpass tool to generate password hashes:
# Generate a password hash
./hashpass "YourSecurePassword123!"
# Output:
# Password hash generated successfully!
# Add this to your wafway.yaml configuration:
#
# dashboard:
# password_hash: "$2a$12$xxx..."
Add the generated hash to your configuration:
dashboard:
enabled: true
username: "admin"
password_hash: "$2a$12$Z8IZk0PNoxgQm9UUNwESi.eHEgKrQKPlB9UiIzrBZSg8NiNQgZQ.O"
Password Requirements: Passwords must contain at least 8 characters with uppercase, lowercase, numbers, and special characters.
Secure Tokens
WafWay generates cryptographically secure session tokens using Go's crypto/rand package. Tokens are:
- 256-bit random values (32 bytes)
- Base64-URL encoded for safe transmission
- Valid for 24 hours by default
- Automatically cleaned up when expired
Security Headers
WafWay automatically adds essential security headers to all responses to protect against common web vulnerabilities. These headers are configurable and enabled by default.
Default Headers: WafWay automatically sets X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, and Permissions-Policy headers on all proxied responses.
| Header | Default Value | Purpose |
|---|---|---|
X-Content-Type-Options |
nosniff |
Prevents MIME type sniffing attacks |
X-Frame-Options |
SAMEORIGIN |
Prevents clickjacking attacks |
X-XSS-Protection |
1; mode=block |
Enables browser XSS filter |
Referrer-Policy |
strict-origin-when-cross-origin |
Controls referrer information leakage |
Permissions-Policy |
geolocation=(), microphone=(), camera=() |
Restricts browser features |
WafWay also automatically removes identifying headers from backend responses:
Server- Removed to hide server softwareX-Powered-By- Removed to hide technology stackX-AspNet-Version- Removed for ASP.NET applications
HSTS Configuration
HTTP Strict Transport Security (HSTS) forces browsers to only connect via HTTPS, preventing downgrade attacks and cookie hijacking. WafWay supports full HSTS configuration.
# HSTS Configuration in wafway.yaml
security_headers:
hsts:
enabled: true # Enable HSTS header
max_age: 63072000 # 2 years (recommended)
include_subdomains: true # Apply to all subdomains
preload: false # Enable for HSTS preload list submission
HSTS Options
| Option | Default | Description |
|---|---|---|
enabled |
true |
Enable or disable HSTS header |
max_age |
63072000 |
Time in seconds browsers should remember HTTPS-only (2 years recommended) |
include_subdomains |
true |
Apply HSTS to all subdomains |
preload |
false |
Add preload directive for browser preload lists |
Warning: Before enabling preload, ensure your entire domain and all subdomains support HTTPS. Once added to preload lists, removal is difficult and can take months.
Example Output
# With default settings, WafWay adds:
Strict-Transport-Security: max-age=63072000; includeSubDomains
# With preload enabled:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content Security Policy (CSP)
Content Security Policy helps prevent XSS, clickjacking, and code injection attacks by specifying which content sources are allowed. WafWay provides comprehensive CSP configuration.
# CSP Configuration in wafway.yaml
security_headers:
csp:
enabled: true
default_src: ["'self'"]
script_src: ["'self'"]
style_src: ["'self'", "'unsafe-inline'"]
img_src: ["'self'", "data:", "https:"]
font_src: ["'self'", "https://fonts.gstatic.com"]
connect_src: ["'self'", "https://api.example.com"]
frame_ancestors: ["'none'"]
form_action: ["'self'"]
base_uri: ["'self'"]
object_src: ["'none'"]
report_uri: "/csp-report" # Optional: endpoint for violation reports
report_only: false # Set true to test without blocking
CSP Directives
| Directive | Default | Description |
|---|---|---|
default_src |
['self'] |
Fallback for other directives |
script_src |
['self'] |
Allowed JavaScript sources |
style_src |
['self', 'unsafe-inline'] |
Allowed CSS sources |
img_src |
['self', 'data:'] |
Allowed image sources |
font_src |
['self'] |
Allowed font sources |
connect_src |
['self'] |
Allowed XHR/WebSocket/fetch targets |
frame_ancestors |
['none'] |
Who can embed this page (clickjacking protection) |
form_action |
['self'] |
Allowed form submission targets |
base_uri |
['self'] |
Allowed base URL for relative URLs |
object_src |
['none'] |
Allowed plugin sources (Flash, Java) |
CSP Source Values
'self'- Same origin only'none'- Block all sources'unsafe-inline'- Allow inline scripts/styles (avoid if possible)'unsafe-eval'- Allow eval() (avoid if possible)https:- Any HTTPS URLdata:- Data URIs (for inline images)https://example.com- Specific domain
Testing CSP: Set report_only: true to test your CSP policy without breaking your site. Violations will be logged but not blocked.
Example Output
# WafWay generates a CSP header like:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; object-src 'none'
CORS Whitelist Configuration
Cross-Origin Resource Sharing (CORS) controls which domains can make requests to your API. WafWay uses a whitelist approach for security - no wildcards allowed.
Security Note: WafWay does NOT support wildcard (*) CORS origins. You must explicitly list allowed origins. This prevents unauthorized cross-origin access to your admin API.
# CORS Configuration for Admin Portal in wafway.yaml
admin_portal:
enabled: true
listen: ":9090"
cors:
allowed_origins:
- "https://admin.example.com"
- "https://dashboard.example.com"
- "http://localhost:3000" # For local development
allowed_methods:
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "OPTIONS"
allowed_headers:
- "Accept"
- "Authorization"
- "Content-Type"
- "X-Requested-With"
exposed_headers: [] # Headers browser can access
allow_credentials: true # Allow cookies/auth headers
max_age: 86400 # Preflight cache: 24 hours
CORS Options
| Option | Default | Description |
|---|---|---|
allowed_origins |
[] (same-origin only) |
List of allowed origins (no wildcards) |
allowed_methods |
[GET, POST, PUT, DELETE, OPTIONS] |
Allowed HTTP methods |
allowed_headers |
[Accept, Authorization, Content-Type, X-Requested-With] |
Headers the client can send |
exposed_headers |
[] |
Headers exposed to the browser |
allow_credentials |
true |
Allow cookies and auth headers |
max_age |
86400 |
Preflight response cache time (seconds) |
CORS Behavior
- Empty whitelist: Only same-origin requests allowed (most secure)
- Origin not in whitelist: Request proceeds but no CORS headers set (browser blocks cross-origin response)
- Origin in whitelist: Full CORS headers returned, cross-origin request allowed
Example Scenarios
# Scenario 1: Same-origin only (default - most secure)
admin_portal:
cors:
allowed_origins: [] # Empty = same-origin only
# Scenario 2: Allow specific dashboard domain
admin_portal:
cors:
allowed_origins:
- "https://admin.mycompany.com"
# Scenario 3: Development + Production
admin_portal:
cors:
allowed_origins:
- "https://admin.mycompany.com" # Production
- "https://staging.mycompany.com" # Staging
- "http://localhost:3000" # Local dev
Best Practice: Keep the allowed_origins list as small as possible. Remove development origins before deploying to production.
Creating Custom Rules
WafWay supports custom rules with full CRUD operations and database persistence:
# Create a custom rule via API
curl -X POST http://localhost:8080/api/v1/rules \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Block Admin Access",
"description": "Block direct access to admin paths",
"pattern": "^/admin",
"action": "block",
"target": "uri",
"enabled": true,
"priority": 100
}'
Rule Patterns
Custom rules support regular expressions for pattern matching:
| Target | Description |
|---|---|
uri |
Match against the request URI |
body |
Match against the request body |
headers |
Match against request headers |
all |
Match against URI, body, and headers |
Actions
block- Block the request and return 403log- Log the request but allow it throughallow- Explicitly allow (whitelist)
Rules API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/rules |
List all custom rules |
| POST | /api/v1/rules |
Create a new rule |
| PUT | /api/v1/rules/{id} |
Update an existing rule |
| DELETE | /api/v1/rules/{id} |
Delete a rule |
| POST | /api/v1/rules/{id}/toggle |
Toggle rule enabled/disabled |
Traffic Data
WafWay collects and aggregates traffic data for analytics. Access via the REST API:
# Get traffic data for the last 24 hours
curl http://localhost:8080/api/v1/traffic \
-H "Authorization: Bearer YOUR_TOKEN"
# Response
[
{
"timestamp": "2026-01-02T12:00:00Z",
"total_requests": 1250,
"blocked_count": 45,
"allowed_count": 1205,
"bytes_in": 524288,
"bytes_out": 2097152,
"avg_latency_ms": 12.5
}
]
Top Paths
# Get most accessed paths
curl "http://localhost:8080/api/v1/traffic/top-paths?limit=10" \
-H "Authorization: Bearer YOUR_TOKEN"
# Response
[
{
"path": "/api/users",
"request_count": 5420,
"blocked_count": 12,
"avg_latency_ms": 8.3
}
]
Attack Logs
All blocked requests are logged with full details:
# Get recent attacks
curl "http://localhost:8080/api/v1/traffic/attacks?limit=50" \
-H "Authorization: Bearer YOUR_TOKEN"
# Response
[
{
"id": "1735812345-123456789",
"timestamp": "2026-01-02T14:25:45Z",
"source_ip": "192.168.1.100",
"method": "GET",
"path": "/api/users?id=1' OR '1'='1",
"attack_type": "sqli",
"rule_name": "SQL Injection Detection",
"severity": "high",
"action": "blocked"
}
]
Storage Configuration
WafWay uses SQLite for persistent storage of rules, attack logs, and traffic data:
storage:
type: "sqlite"
path: "/var/lib/wafway/wafway.db"
# Data retention settings (optional)
# Older data is automatically cleaned up
retention:
request_logs: 7d # Keep request logs for 7 days
attack_logs: 30d # Keep attack logs for 30 days
traffic_stats: 90d # Keep aggregated stats for 90 days
Enterprise Feature: PostgreSQL and MySQL storage backends are available in the Enterprise edition for high-availability deployments.
API Overview
WafWay provides a REST API for programmatic access to all features.
Base URL: http://your-server:8080/api/v1
Authentication
All API requests require a Bearer token:
# Login to get token
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "your-password"}'
# Response
{
"token": "xxx-your-token-xxx",
"expires_at": "2026-01-03T00:00:00Z"
}
# Use token in requests
curl http://localhost:8080/api/v1/stats \
-H "Authorization: Bearer xxx-your-token-xxx"
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/stats |
Get current statistics |
| GET | /api/v1/health |
Health check status |
| GET | /api/v1/ips/whitelist |
List whitelisted IPs |
| POST | /api/v1/ips/whitelist |
Add IP to whitelist |
| GET | /api/v1/rules |
List all rules |
| POST | /api/v1/rules |
Create new rule |
| GET | /api/v1/logs |
Get request logs |
| GET | /api/v1/config |
Get configuration |
Need help? Join our community Discord or open an issue on GitHub.