Nginx (pronounced "engine-x") is a powerful, high-performance web server that has become the de facto standard for web application delivery. This guide will walk you through everything you need to know about Nginx, from core concepts to advanced configuration, with a special focus on PHP/Laravel applications.
📋 Table of Contents
- Introduction
- What is Nginx?
- Core Concepts
- Installation and Basic Configuration
- Configuration Structure
- Common Use Cases
- Performance Optimization
- Security Best Practices
- PHP/Laravel Integration
- Docker Integration
- Troubleshooting
- Conclusion
What is Nginx?
Nginx is an open-source web server that can also be used as a reverse proxy, load balancer, and HTTP cache. It was created by Igor Sysoev and publicly released in 2004. Nginx is known for its high performance, stability, rich feature set, simple configuration, and low resource consumption.
Why Choose Nginx?
- High performance and low memory usage
- Excellent handling of concurrent connections
- Built-in load balancing capabilities
- Strong security features
- Extensive documentation and community support
Core Concepts
Key Components
- Directives: Configuration commands that control Nginx's behavior
- Contexts: Blocks that group related directives
- Events: How Nginx handles connections
- HTTP: Web server configuration
- Server: Virtual host configuration
- Location: URL matching and handling
Basic Configuration Structure
# Main context
user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Events context
events {
worker_connections 1024;
}
# HTTP context
http {
# Basic settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# MIME types
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Server context
server {
listen 80;
server_name example.com;
root /var/www/html;
# Location context
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
}
Installation and Basic Configuration
Ubuntu/Debian Installation
# Update package list
sudo apt update
# Install Nginx
sudo apt install nginx
# Start Nginx
sudo systemctl start nginx
# Enable Nginx to start on boot
sudo systemctl enable nginx
# Check status
sudo systemctl status nginx
Basic Configuration File Structure
/etc/nginx/
├── nginx.conf # Main configuration file
├── sites-available/ # Available site configurations
├── sites-enabled/ # Enabled site configurations
├── conf.d/ # Additional configuration files
└── snippets/ # Reusable configuration snippets
Configuration Structure
Main Configuration (nginx.conf)
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# MIME Types
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip Settings
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Virtual Host Configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Virtual Host Configuration
# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public;
index index.php index.html;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
# Logging
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Common Use Cases
1. Static File Serving
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, no-transform";
}
2. Load Balancing
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
server {
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
3. SSL/TLS Configuration
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000" always;
}
Performance Optimization
1. Worker Processes and Connections
worker_processes auto; # Automatically set based on CPU cores
worker_rlimit_nofile 65535; # Increase file descriptor limit
events {
worker_connections 1024;
multi_accept on;
use epoll; # Efficient event processing method
}
2. Caching
# FastCGI Cache
fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
location ~ \.php$ {
fastcgi_cache my_cache;
fastcgi_cache_valid 200 60m;
fastcgi_cache_use_stale error timeout http_500 http_503;
fastcgi_cache_key "$request_method$request_uri";
}
3. Gzip Compression
Gzip compression reduces the size of files sent from the server to the client, which can significantly decrease the time it takes for a web page to load. This is especially beneficial for users with slower internet connections. By enabling gzip, you can improve the performance and speed of your website.
Security Headers
-
X-Frame-Options: This header helps prevent clickjacking attacks by controlling whether a browser should be allowed to render a page in a
<frame>,<iframe>,<embed>, or<object>. Setting it toSAMEORIGINallows the page to be displayed only on the same origin as the page itself. -
X-Content-Type-Options: This header prevents browsers from MIME-sniffing a response away from the declared content-type. Setting it to
nosniffhelps prevent attacks based on MIME-type confusion. -
X-XSS-Protection: This header enables the Cross-Site Scripting (XSS) filter built into most modern web browsers. Setting it to
1; mode=blockwill block the page if an XSS attack is detected. -
Referrer-Policy: This header controls how much referrer information should be included with requests. Setting it to
strict-origin-when-cross-originprovides a good balance between security and usability.
Client Body and Header Timeouts
These settings control how long the server will wait for a client to send the body and headers of a request. By setting appropriate timeouts, you can prevent slow clients from holding connections open indefinitely, which can help protect against certain types of denial-of-service (DoS) attacks.
Security Best Practices
1. Basic Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
2. Rate Limiting
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /login {
limit_req zone=one burst=5 nodelay;
proxy_pass http://backend;
}
}
3. SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
PHP/Laravel Integration
1. Standard Installation (Without Docker)
# /etc/nginx/sites-available/laravel
server {
listen 80;
server_name laravel.example.com;
root /var/www/laravel/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
2. Docker Integration
# docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/ssl:/etc/nginx/ssl
depends_on:
- php
- mysql
php:
image: php:8.3-fpm
depends_on:
- mysql
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: app
# docker/nginx/conf.d/default.conf
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php index.html;
charset utf-8;
client_max_body_size 100M;
client_body_buffer_size 100M;
client_body_timeout 60s;
client_header_timeout 60s;
send_timeout 60s;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
gzip_disable "MSIE [1-6]\.";
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: *.example.com wss://*.example.com; img-src 'self' data: https:; font-src 'self' data: https:;";
# Logs
access_log /var/www/html/storage/logs/nginx_access.log;
error_log /var/www/html/storage/logs/nginx_error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_read_timeout 600;
fastcgi_send_timeout 600;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Troubleshooting
Common Issues and Solutions
-
502 Bad Gateway
- Check if PHP-FPM is running
- Verify socket file exists
- Check permissions
-
404 Not Found
- Verify root directory path
- Check file permissions
- Ensure try_files directive is correct
-
Permission Issues
# Fix permissions for Laravel sudo chown -R www-data:www-data /var/www/laravel sudo chmod -R 755 /var/www/laravel sudo chmod -R 775 /var/www/laravel/storage sudo chmod -R 775 /var/www/laravel/bootstrap/cache
Useful Commands
# Test configuration
nginx -t
# Reload configuration
sudo systemctl reload nginx
# Check error logs
tail -f /var/log/nginx/error.log
# Check access logs
tail -f /var/log/nginx/access.log
Conclusion
Nginx is a powerful and flexible web server that can handle various use cases, from serving static files to complex load balancing scenarios. By following the best practices outlined in this guide, you can create a secure, performant, and maintainable web server configuration for your PHP/Laravel applications.
Remember to:
- Always test configurations before applying them
- Keep security in mind
- Monitor performance
- Regularly update Nginx and its modules
- Back up your configurations
Additional Resources
Basic Authentication
Basic authentication can be used to restrict access to certain parts of your website. To set up basic authentication in Nginx, follow these steps:
-
Create a Password File
- Use the
htpasswdcommand to create a password file:sudo htpasswd -c /etc/nginx/.htpasswd username - You will be prompted to enter a password for the user.
- Use the
-
Update Nginx Configuration
- Add the following lines to your server block in the Nginx configuration:
location / { auth_basic "Restricted Content"; auth_basic_user_file /etc/nginx/.htpasswd; try_files $uri $uri/ /index.php?$query_string; }
- Add the following lines to your server block in the Nginx configuration:
-
Reload Nginx
- After updating the configuration, reload Nginx to apply the changes:
sudo systemctl reload nginx
- After updating the configuration, reload Nginx to apply the changes:
This setup will prompt users to enter a username and password when accessing the restricted area.
Installing apache2-utils
To use the htpasswd command for creating password files, you need to install the apache2-utils package. This can be done with the following command:
sudo apt install apache2-utils
Handling .htpasswd in Docker
When using Docker, you have a couple of options for handling the .htpasswd file:
-
Store in Repository:
- You can store the
.htpasswdfile in your repository if it doesn't contain sensitive information or if you are using environment variables to manage credentials securely.
- You can store the
-
Generate During Build:
- Alternatively, you can generate the
.htpasswdfile during the Docker build process. This can be done by adding a command in your Dockerfile to create the file usinghtpasswd.
- Alternatively, you can generate the
-
Use Docker Secrets:
- For a more secure approach, consider using Docker secrets to manage sensitive files like
.htpasswd. This keeps the file out of your repository and ensures it's only accessible to the containers that need it.
- For a more secure approach, consider using Docker secrets to manage sensitive files like
Example Dockerfile Command
To generate the .htpasswd file during the build, you can add a command like this to your Dockerfile:
RUN apt-get update && apt-get install -y apache2-utils \
&& htpasswd -bc /etc/nginx/.htpasswd username password
Replace username and password with your desired credentials. This command installs apache2-utils and creates the .htpasswd file with the specified user credentials.
Follow me on LinkedIn for more Laravel and DevOps content!
Would you like to learn more about instant search? Leave a comment below!