⚠️ Version Policy Note
H2O no longer uses version tagging since January 2020. The master branch is considered stable. Configuration syntax may change between commits. Always test configuration changes before deploying.
| Installation Method | Configuration Path |
|---|---|
| Source Build | /usr/local/etc/h2o/h2o.conf |
| Debian Package | /etc/h2o/h2o.conf |
| RHEL Package | /etc/h2o/h2o.conf |
| Docker | /etc/h2o/h2o.conf (mounted volume) |
| macOS (Homebrew) | /usr/local/etc/h2o/h2o.conf |
H2O uses YAML for configuration. The configuration file is reloaded on SIGHUP.
# Global settings
max-connections: 1024
num-threads: 4
# Listen directives
listen:
port: 80
# Host definitions
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
# /etc/h2o/h2o.conf
listen:
port: 80
hosts:
"default":
paths:
"/":
file.dir: /var/www/html
file.index: [index.html, index.htm]
listen:
port: 443
ssl:
certificate-file: /etc/ssl/certs/example.com.crt
key-file: /etc/ssl/private/example.com.key
dh-file: /etc/ssl/dhparam.pem # Optional, for stronger DH
minimum-version: TLSv1.2
cipher-preference: server
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
openssl dhparam -out /etc/ssl/dhparam.pem 2048
listen:
port: 443
ssl:
certificate-file: /etc/ssl/certs/example.com.crt
key-file: /etc/ssl/private/example.com.key
http3: true
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
⚠️ Note: HTTP/3 requires UDP port 443 to be open. Test thoroughly before enabling in production.
listen:
port: 80
hosts:
"example.com":
paths:
"/":
proxy.reverse.url: http://127.0.0.1:3000/
proxy.preserve-host: true
listen:
port: 80
hosts:
"example.com":
paths:
"/":
proxy.reverse.url:
- http://127.0.0.1:3001/
- http://127.0.0.1:3002/
- http://127.0.0.1:3003/
proxy.timeout.keepalive: 30
listen:
port: 80
hosts:
"example.com":
paths:
"/ws":
proxy.reverse.url: http://127.0.0.1:3000/ws
proxy.upgrade: websocket
listen:
port: 443
ssl:
certificate-file: /etc/ssl/certs/example.com.crt
key-file: /etc/ssl/private/example.com.key
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
header.add:
strict-transport-security: "max-age=31536000; includeSubDomains; preload"
x-content-type-options: "nosniff"
x-frame-options: "SAMEORIGIN"
x-xss-protection: "1; mode=block"
content-security-policy: "default-src 'self'"
referrer-policy: "strict-origin-when-cross-origin"
permissions-policy: "geolocation=(), microphone=(), camera=()"
access-log: /var/log/h2o/access.log
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
access-log:
path: /var/log/h2o/access.log
format: '%v:%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"'
access-log:
- path: /var/log/h2o/access.log
format: combined
- path: /var/log/h2o/json-access.log
format: '{"time":"%t","host":"%v","client":"%h","method":"%m","path":"%U","status":%s,"bytes":%b}'
error-log: /var/log/h2o/error.log
log-level: warn # debug, info, warn, error, critical
listen:
port: 80
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/example.com
"www.example.com":
redirect:
status: 301
url: https://example.com/
"api.example.com":
paths:
"/":
proxy.reverse.url: http://127.0.0.1:3000/
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
redirect:
- match: /old-path/(.*)
status: 301
url: /new-path/$1
- match: /\.ht
status: 404
listen:
port: 80
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
file.send-compressed: ON
"*.php":
fastcgi.connect:
host: /var/run/php/php-fpm.sock
type: unix
listen:
port: 80
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
"/api":
mruby.handler: |
Proc.new do |env|
[200, {'content-type' => 'application/json'}, ['{"status":"ok"}']]
end
hosts:
"example.com":
paths:
"/":
file.dir: /var/www/html
filter:
- name: rate-limit
config:
burst: 100
rate: 50
burst-delay: 100ms
delay: 20ms
| Setting | Description |
|---|---|
listen.port |
TCP port for incoming connections |
listen.ssl |
Enable TLS/SSL on this listener |
ssl.certificate-file |
Path to SSL certificate (PEM format) |
ssl.key-file |
Path to SSL private key |
ssl.dh-file |
Diffie-Hellman parameters for stronger key exchange |
ssl.minimum-version |
Minimum TLS version (TLSv1.0, TLSv1.1, TLSv1.2, TLSv1.3) |
file.dir |
Document root directory |
file.index |
List of index files to serve |
proxy.reverse.url |
Backend URL for reverse proxy |
fastcgi.connect |
FastCGI backend connection settings |
header.add |
Add HTTP response headers |
access-log |
Access log file path |
error-log |
Error log file path |
log-level |
Logging verbosity (debug, info, warn, error, critical) |
sudo h2o -c /etc/h2o/h2o.conf -t
Expected output:
Configuration OK
# Using systemctl
sudo systemctl reload h2o
# Or send SIGHUP
sudo kill -HUP $(cat /var/run/h2o/h2o.pid)
sudo systemctl status h2o
curl -I http://localhost
curl -Ik https://localhost # For HTTPS
# Configuration files
sudo chown root:root /etc/h2o/h2o.conf
sudo chmod 644 /etc/h2o/h2o.conf
# SSL certificates
sudo chown root:root /etc/ssl/private/*.key
sudo chmod 600 /etc/ssl/private/*.key
# Log directory
sudo chown h2o:adm /var/log/h2o
sudo chmod 755 /var/log/h2o
Squeezing every bit of performance from your H2O installation? Our experts help with:
Optimize your setup: office@linux-server-admin.com | Contact Us