Install and configure H2O HTTP/2 web server with FastCGI and SSL certificates

Intermediate 25 min Apr 03, 2026 13 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Deploy H2O, a high-performance HTTP/2 web server with native FastCGI support and SSL/TLS termination. Configure virtual hosts, PHP-FPM integration, and performance optimization for production environments.

Prerequisites

  • Root or sudo access
  • Basic Linux command line knowledge
  • Understanding of web server concepts

What this solves

H2O is a modern, high-performance web server designed for HTTP/2 and HTTPS from the ground up. It provides faster response times than traditional web servers while consuming less memory and CPU resources. This tutorial shows you how to install H2O, configure virtual hosts with SSL certificates, set up FastCGI integration with PHP-FPM, and optimize performance for production workloads.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest security patches and dependencies.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install build dependencies

H2O requires compilation tools and SSL libraries. Install the necessary development packages for building from source.

sudo apt install -y build-essential cmake pkg-config libssl-dev zlib1g-dev libyaml-dev
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y cmake pkgconfig openssl-devel zlib-devel libyaml-devel

Create H2O user and directories

Create a dedicated system user for H2O and set up the required directory structure with proper ownership.

sudo useradd --system --home /var/lib/h2o --shell /bin/false h2o
sudo mkdir -p /etc/h2o /var/log/h2o /var/lib/h2o /var/www/html
sudo chown h2o:h2o /var/log/h2o /var/lib/h2o
sudo chown www-data:www-data /var/www/html

Download and compile H2O from source

Download the latest stable release of H2O and compile it with HTTP/2 and SSL support enabled.

cd /tmp
wget https://github.com/h2o/h2o/archive/v2.2.6.tar.gz
tar xzf v2.2.6.tar.gz
cd h2o-2.2.6
cmake -DWITH_BUNDLED_SSL=on -DCMAKE_INSTALL_PREFIX=/usr/local .
make -j$(nproc)
sudo make install

Install PHP-FPM for FastCGI support

Install PHP-FPM to handle dynamic content through FastCGI. This provides better performance than traditional CGI.

sudo apt install -y php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip
sudo dnf install -y php-fpm php-mysqlnd php-curl php-gd php-mbstring php-xml php-zip

Create H2O main configuration

Set up the primary H2O configuration file with basic settings for HTTP/2, SSL, and logging.

user: h2o
pid-file: /var/run/h2o.pid
error-log: /var/log/h2o/error.log
access-log: /var/log/h2o/access.log

Global settings

max-connections: 1024 num-threads: 4 http2-idle-timeout: 10 http2-graceful-shutdown-timeout: 30

Default host

hosts: "example.com:80": listen: port: 80 paths: "/": file.dir: /var/www/html file.index: ['index.html', 'index.php'] fastcgi.connect: port: 9000 type: tcp fastcgi.spawn: "php-fpm" "example.com:443": listen: port: 443 ssl: certificate-file: /etc/ssl/certs/example.com.crt key-file: /etc/ssl/private/example.com.key cipher-suite: "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256" cipher-preference: server paths: "/": file.dir: /var/www/html file.index: ['index.html', 'index.php'] fastcgi.connect: port: 9000 type: tcp fastcgi.spawn: "php-fpm" header.add: "Strict-Transport-Security: max-age=31536000; includeSubDomains" header.add: "X-Frame-Options: DENY" header.add: "X-Content-Type-Options: nosniff"

Generate SSL certificates

Create self-signed SSL certificates for testing. In production, use certificates from a trusted Certificate Authority or Let's Encrypt.

sudo mkdir -p /etc/ssl/private
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout /etc/ssl/private/example.com.key \
  -out /etc/ssl/certs/example.com.crt \
  -subj "/C=US/ST=State/L=City/O=Organization/CN=example.com"
sudo chmod 600 /etc/ssl/private/example.com.key
sudo chmod 644 /etc/ssl/certs/example.com.crt
Note: Never use chmod 777 for SSL certificates. Private keys should have 600 permissions (readable only by owner), while certificates can have 644 permissions.

Configure PHP-FPM pool

Optimize the PHP-FPM pool configuration for better performance with H2O.

[www]
user = www-data
group = www-data
listen = 127.0.0.1:9000
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500

php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
security.limit_extensions = .php

Create systemd service file

Set up a systemd service to manage H2O with automatic restart and proper dependency management.

[Unit]
Description=H2O HTTP/2 Server
After=network.target remote-fs.target nss-lookup.target
Wants=network.target

[Service]
Type=simple
User=h2o
Group=h2o
ExecStart=/usr/local/bin/h2o -c /etc/h2o/h2o.conf
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
KillSignal=SIGTERM
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/h2o /var/run
NoNewPrivileges=true
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Create test PHP file

Create a simple PHP file to test FastCGI integration and verify that dynamic content processing works correctly.

sudo chown www-data:www-data /var/www/html/info.php
sudo chmod 644 /var/www/html/info.php

Configure log rotation

Set up logrotate to manage H2O log files and prevent disk space issues.

/var/log/h2o/*.log {
    daily
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 644 h2o h2o
    postrotate
        /bin/kill -s USR1 cat /var/run/h2o.pid 2> /dev/null 2> /dev/null || true
    endscript
}

Start and enable services

Start PHP-FPM and H2O services, then enable them to start automatically on system boot.

sudo systemctl daemon-reload
sudo systemctl enable --now php8.1-fpm
sudo systemctl enable --now h2o
sudo systemctl status h2o
sudo systemctl status php8.1-fpm

Configure firewall rules

Open the necessary ports for HTTP and HTTPS traffic while maintaining security.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Performance optimization

Optimize system limits

Increase system limits for file descriptors and connections to handle high traffic loads.

h2o soft nofile 65536
h2o hard nofile 65536
h2o soft nproc 32768
h2o hard nproc 32768

Tune kernel parameters

Optimize network and memory settings for better HTTP/2 performance. These settings improve connection handling and reduce latency.

net.core.somaxconn = 65536
net.ipv4.tcp_max_syn_backlog = 65536
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 3
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
sudo sysctl -p /etc/sysctl.d/99-h2o.conf

Security hardening

Configure HTTP security headers

Add security headers to protect against common web vulnerabilities. Update your H2O configuration with enhanced security settings.

# Security headers configuration
header.add: "X-Frame-Options: DENY"
header.add: "X-Content-Type-Options: nosniff"
header.add: "X-XSS-Protection: 1; mode=block"
header.add: "Referrer-Policy: strict-origin-when-cross-origin"
header.add: "Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
header.add: "Permissions-Policy: geolocation=(), microphone=(), camera=()"

Hide server version

header.add: "Server: H2O"

Set up access logging

Configure detailed access logging for security monitoring and performance analysis.

# Enhanced access log format
access-log:
  path: /var/log/h2o/access.log
  format: '%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-agent}i" %{duration}x %{ssl.protocol-version}x'

For additional security, consider integrating H2O with AppArmor security profiles to enforce mandatory access controls.

Verify your setup

Test your H2O installation to ensure HTTP/2, SSL, and FastCGI are working correctly.

# Check H2O service status
sudo systemctl status h2o

Verify HTTP/2 support

curl -I --http2 -k https://localhost/

Test FastCGI with PHP

curl -k https://localhost/info.php | head -20

Check SSL certificate

openssl s_client -connect localhost:443 -servername example.com < /dev/null

Monitor connections and performance

sudo netstat -tlnp | grep h2o sudo journalctl -u h2o -f

Common issues

SymptomCauseFix
H2O fails to startConfiguration syntax errorsudo /usr/local/bin/h2o -t -c /etc/h2o/h2o.conf
PHP files download instead of executingFastCGI not configured correctlyCheck PHP-FPM status and port 9000 binding
SSL certificate errorsWrong certificate path or permissionsVerify certificate files exist and have correct ownership
High CPU usageToo many worker threadsAdjust num-threads to match CPU cores
Connection refused errorsFirewall blocking portsCheck firewall rules for ports 80 and 443
File permission deniedWrong file ownershipsudo chown -R www-data:www-data /var/www/html
Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions like 644 for files and 755 for directories.

Next steps

Automated install script

Run this to automate the entire setup

#h2o #http2 #web-server #ssl #fastcgi #php-fpm #performance #nginx-alternative

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer