
Did you know that a Raspberry Pi can handle thousands of daily visitors and serve as a production-ready web server? With the right setup, this tiny computer can power websites, web applications, and even serve as your personal development environment.
In this comprehensive guide, I'll walk you through setting up a robust Nginx server with PHP on your Raspberry Pi.
Why Choose Raspberry Pi for Web Hosting?
Before we dive into the technical setup, let's understand why Raspberry Pi makes an excellent web server:
- Cost-effective: A complete setup costs under €100, compared to hundreds for traditional hosting
- Energy efficient: Uses only 2-5 watts of power, perfect for 24/7 operation
- Full control: Complete ownership of your server and data
- Learning opportunity: Great way to understand server administration
- Scalable: Can handle multiple websites and applications
Prerequisites
Before starting, ensure you have:
- Raspberry Pi (3B+, 4B, or newer recommended)
- MicroSD card (32GB or larger, Class 10)
- Power supply for your Pi
- Network connection (Ethernet or WiFi)
- Raspberry Pi OS (formerly Raspbian) installed
Step 1: Install Raspberry Pi OS
First, you need to install Raspberry Pi OS on your MicroSD card. You can download the latest version from the Raspberry Pi Imager at https://www.raspberrypi.com/software/.
Install Raspberry Pi Imager and follow the instructions to install Raspberry Pi OS Lite 32bit on your MicroSD card. Enable SSH and set up wifi connection if needed.
Insert the MicroSD card into your Raspberry Pi and power it on.
Step 2: Connect to your Raspberry Pi
Connect to your Raspberry Pi using SSH or a serial console. You can use the following command to connect to your Raspberry Pi:
pi@raspberrypi:~$ssh [your_username]@[your_pi_ip]
If using Windows, you can use PuTTY to connect to your Raspberry Pi.
Step 3: Update Your System
First, let's ensure your Raspberry Pi is up to date:
pi@raspberrypi:~$sudo apt update && sudo apt full-upgrade
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
This ensures you have the latest security patches and software versions and enables automatic security updates.
Step 4: Install Firewall
Install and configure UFW (Uncomplicated Firewall) to secure your Raspberry Pi:
pi@raspberrypi:~$sudo apt install ufw
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
This will allow SSH, HTTP, and HTTPS traffic to your Raspberry Pi.
Step 5: Install Nginx
Nginx is a high-performance web server that's perfect for Raspberry Pi:
pi@raspberrypi:~$sudo apt install nginx
After installation, start and enable Nginx:
pi@raspberrypi:~$sudo systemctl start nginx
sudo systemctl enable nginx
Test if Nginx is running by visiting your Pi's IP address in a browser. You should see the default Nginx welcome page.
Step 6: Install PHP and PHP-FPM
PHP-FPM (FastCGI Process Manager) is the recommended way to run PHP with Nginx:
pi@raspberrypi:~$sudo apt install php php-fpm
This installs PHP 8.x (latest stable version) along with common extensions.
View the installed PHP version:
pi@raspberrypi:~$ls /var/run/php/
Start and enable PHP-FPM:
pi@raspberrypi:~$sudo systemctl start php8.2-fpm
sudo systemctl enable php8.2-fpm
Note: Replace "8.2" with your installed PHP version if different.
Step 7: Install Certbot
Install Certbot to manage SSL certificates:
pi@raspberrypi:~$sudo apt install certbot python3-certbot-nginx
This will install Certbot and the Nginx plugin.
Step 8: Port Forwarding
If you want to access your Raspberry Pi from outside your local network, you need to forward ports. For example, if you want to access your Raspberry Pi from your phone, you need to forward port 80 to your Raspberry Pi.
Forward ports 22 (SSH), 80 (HTTP) and 443 (HTTPS) on your router to your Raspberry Pi’s local IP.
Step 9: Domain Name
You need a domain name (e.g., mysite.com) that points to your Pi’s public IP.
Use a Dynamic DNS provider if your IP changes (e.g., DuckDNS, No-IP).
Update DNS A-record to point to your public IP.
Step 10: Make a directory for your website(s)
Create a directory for your website(s):
pi@raspberrypi:~$sudo mkdir -p /var/www/mysite.com/html
If creating multiple websites, you can create a directory for each website:
pi@raspberrypi:~$sudo mkdir -p /var/www/mysite1.com/html
sudo mkdir -p /var/www/mysite2.com/html
Set permissions:
pi@raspberrypi:~$sudo chmod -R 777 /var/www/mysite.com/html
sudo chown -R www-data:www-data /var/www/mysite.com/html
If creating multiple websites, you can set permissions for each website:
pi@raspberrypi:~$sudo chmod -R 777 /var/www/mysite1.com/html
sudo chown -R www-data:www-data /var/www/mysite1.com/html
sudo chmod -R 777 /var/www/mysite2.com/html
sudo chown -R www-data:www-data /var/www/mysite2.com/html
Step 11: Configure Nginx website(s)
Create a new server block configuration:
pi@raspberrypi:~$sudo nano /etc/nginx/sites-available/mysite.com
Add your site configuration:
pi@raspberrypi:~$server {
listen 80;
server_name mysite.com www.mysite.com;
root /var/www/mysite.com/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
If creating multiple websites, you can create a new server block configuration for each website:
pi@raspberrypi:~$sudo nano /etc/nginx/sites-available/mysite1.com
Add your site configuration:
pi@raspberrypi:~$server {
listen 80;
server_name mysite1.com www.mysite1.com;
root /var/www/mysite1.com/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
Second website:
pi@raspberrypi:~$sudo nano /etc/nginx/sites-available/mysite2.com
Add your site configuration:
pi@raspberrypi:~$server {
listen 80;
server_name mysite2.com www.mysite2.com;
root /var/www/mysite2.com/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
Step 12: Enable the website(s)
Enable the website(s):
pi@raspberrypi:~$sudo ln -s /etc/nginx/sites-available/mysite.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/mysite1.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/mysite2.com /etc/nginx/sites-enabled/
Test the configuration:
pi@raspberrypi:~$sudo nginx -t
If the test passes, reload Nginx:
pi@raspberrypi:~$sudo systemctl reload nginx
Visit your website(s) in your browser. You should see the website(s) running.
Step 13: SSL Certificate with Let's Encrypt
Issue an SSL certificate for your website(s):
pi@raspberrypi:~$sudo certbot --nginx -d mysite.com -d www.mysite.com
sudo certbot --nginx -d mysite1.com -d www.mysite1.com
sudo certbot --nginx -d mysite2.com -d www.mysite2.com
This will issue a certificate for your website(s) and configure Nginx to use it.
Step 14: Upload your website(s)
Upload your website(s) to the /var/www/mysite.com/html directory.
If creating multiple websites, you can upload your website(s) to the /var/www/mysite1.com/html and /var/www/mysite2.com/html directories.
You can use FTP, SFTP, or SCP to upload your website(s).
You schould now be possible to access your website(s) at https://mysite.com
, https://mysite1.com
and https://mysite2.com
.
Step 15: Install and configure php mail
Install Mail Transfer Agent (MTA):
pi@raspberrypi:~$sudo apt install msmtp msmtp-mta bsd-mailx
Configure msmtp:
pi@raspberrypi:~$nano /etc/msmtprc
Add the following to the file:
pi@raspberrypi:~$defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp.log
account mail_one
host smtp.gmail.com
port 587
from [your_email]
user [your_email]
password "[your_password]"
account default : mail_one
Edit the file to your needs.
Set permissions:
pi@raspberrypi:~$sudo chown root:root /etc/msmtprc
chmod 777 /etc/msmtprc
Link msmtp to sendmail:
pi@raspberrypi:~$sudo ln -s /usr/bin/msmtp /usr/sbin/sendmail
Add path to sendmail to php.ini:
pi@raspberrypi:~$sudo nano /etc/php/8.2/fpm/php.ini
Add the following to the file:
pi@raspberrypi:~$sendmail_path = /usr/bin/msmtp -t
Restart PHP-FPM:
pi@raspberrypi:~$sudo systemctl restart php8.2-fpm
Restart Nginx:
pi@raspberrypi:~$sudo systemctl restart nginx
You should now be able to send emails from your website.
Step 16: Fail2ban
Fail2ban is a tool that can help yu protect your server from brute force attacks.
Install Fail2ban:
pi@raspberrypi:~$sudo apt install fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
Configure Fail2ban:
pi@raspberrypi:~$sudo nano /etc/fail2ban/jail.local
Add the following to the file:
pi@raspberrypi:~$[DEFAULT]
# who gets the alerts (comma-separated works)
destemail = [your_email]
# who it's from
sender = fail2ban@[your_domain]
sendername = Fail2Ban
# If you installed postfix, leave mta=sendmail (default).
# If you installed msmtp + msmtp-mta, it also provides /usr/sbin/sendmail, so keep sendmail here too.
mta = sendmail
# Choose the email action:
# - %(action_mw)s : ban + whois report + email
# - %(action_mwl)s : ban + whois + log lines + email (more verbose)
# - %(action)s : ban only, no email
action = %(action_mwl)s
banaction = ufw
banaction_allports = ufw
findtime = 10m
# 36d = 36 days / -1 = permanent ban
bantime = 30m
maxretry = 5
# Ignore localhost, IPv6 loopback, and all 192.168.* addresses
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16
# Use journald globally, then override per-jail when needed
backend = systemd
# ---- SSH over journald ----
[sshd]
enabled = true
port = ssh
backend = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service
maxretry = 3
bantime = -1
# ---- Enable built-in nginx filters (recommended) ----
[nginx-http-auth]
enabled = true
port = http,https
backend = auto
logpath = /var/log/nginx/access.log
[nginx-botsearch]
enabled = true
port = http,https
backend = auto
logpath = /var/log/nginx/access.log
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
backend = auto
bantime = -1 ; permanent
findtime = 86400 ; 1 day
Edit destemail, sender and ignoreip to your needs.
Create filters for nginx-http-auth:
pi@raspberrypi:~$sudo nano /etc/fail2ban/filter.d/nginx-http-auth.conf
Add the following to the file:
pi@raspberrypi:~$# fail2ban filter configuration for nginx
[INCLUDES]
before = nginx-error-common.conf
[Definition]
mode = normal
__err_type = <_ertp-<mode>>
_ertp-auth = error
mdre-auth = ^%(__prefix_line)suser "(?:[^"]+|.*?)":? (?:password mismatch|was not found in "[^\"]*"), client: <HOST>, server: \S*, request: "\S+ \S+ HTTP/\d+\.\d+", host: "\S+"(?:, referrer: "\S+")?\s*$
_ertp-fallback = crit
mdre-fallback = ^%(__prefix_line)sSSL_do_handshake\(\) failed \(SSL: error:\S+(?: \S+){1,3} too (?:long|short)\)[^,]*, client: <HOST>
_ertp-normal = %(_ertp-auth)s
mdre-normal = %(mdre-auth)s
_ertp-aggressive = (?:%(_ertp-auth)s|%(_ertp-fallback)s)
mdre-aggressive = %(mdre-auth)s
%(mdre-fallback)s
failregex = <mdre-<mode>>
ignoreregex =
datepattern = {^LN-BEG}
journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
# DEV NOTES:
# mdre-auth:
# Based on samples in https://github.com/fail2ban/fail2ban/pull/43/files
# Extensive search of all nginx auth failures not done yet.
#
# Author: Daniel Black
# mdre-fallback:
# Ban people checking for TLS_FALLBACK_SCSV repeatedly
# https://stackoverflow.com/questions/28010492/nginx-critical-error-with-ssl-handshaking/28010608#28010608
# Author: Stephan Orlowsky
Create filters for nginx-botsearch:
pi@raspberrypi:~$sudo nano /etc/fail2ban/filter.d/nginx-botsearch.conf
Add the following to the file:
pi@raspberrypi:~$# Fail2Ban filter to match web requests for selected URLs that don't exist
#
[INCLUDES]
# Load regexes for filtering
before = botsearch-common.conf
[Definition]
failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) \/<block> \S+\" 404 .+$
^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\, .*?$
ignoreregex =
datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
^[^\[]*\[({DATE})
{^LN-BEG}
journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx
# DEV Notes:
# Based on apache-botsearch filter
#
# Author: Frantisek Sumsal
Test the configuration:
pi@raspberrypi:~$sudo fail2ban-client -t
Schould return: OK: configuration test is successful
You can now start Fail2ban:
pi@raspberrypi:~$sudo fail2ban-client reload
You should now receive emails if someone tries to brute force your server.
You can also check the status of jails from Fail2ban:
pi@raspberrypi:~$sudo fail2ban-client status
View banned ip's in the jail:
pi@raspberrypi:~$sudo fail2ban-client status nginx-http-auth
Unban an ip:
pi@raspberrypi:~$sudo fail2ban-client unban ip_address
You can also check the logs of Fail2ban:
pi@raspberrypi:~$sudo cat /var/log/fail2ban.log
Step 17: Monitoring and Maintenance
Keep your server healthy with these commands:
pi@raspberrypi:~$sudo systemctl status nginx
sudo systemctl status php8.2-fpm
sudo fail2ban-client status
sudo fail2ban-client status sshd
View logs:
pi@raspberrypi:~$sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
journalctl -u ssh
sudo tail -f /var/log/fail2ban.log
Common Issues and Solutions
Issue 1: PHP Files Not Processing
Symptoms: PHP code shows as plain text
Solution: Check PHP-FPM socket path and permissions
pi@raspberrypi:~$sudo ls -la /var/run/php/
sudo chown www-data:www-data /var/run/php/php8.2-fpm.sock
Issue 2: Permission Denied Errors
Symptoms: 403 Forbidden errors
Solution: Fix file permissions
pi@raspberrypi:~$sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html
Issue 3: High Memory Usage
Symptoms: Server becomes slow or unresponsive
Solution: Optimize PHP-FPM settings and add swap space
pi@raspberrypi:~$sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Performance Tips
- Use SSD storage: If possible, use an external SSD for better I/O performance
- Enable caching: Implement Redis or Memcached for dynamic content
- Optimize images: Use WebP format and implement lazy loading
- Minimize plugins: Only install necessary PHP extensions
- Regular updates: Keep your system and software updated
Conclusion
Congratulations! You've successfully set up a production-ready Nginx server with PHP on your Raspberry Pi. This setup can handle:
- Personal blogs and portfolios
- Small business websites
- Development and testing environments
- Web applications and APIs
- Multiple websites with virtual hosting
Your Raspberry Pi web server is now ready to serve websites to the world. Remember to:
- Regularly backup your data
- Monitor system resources
- Keep software updated
- Monitor logs for issues
- Consider setting up automated backups
Ready to take your web development skills to the next level? This Raspberry Pi setup is perfect for learning server administration, testing web applications, and even hosting small production websites.
Need Help with Your Web Server Setup?
If you need assistance with server configuration, optimization, or have questions about hosting your website, I'm here to help. Contact me for professional web development and server administration services.
Get Professional Help